spring-security 3.2.8 配置及部分源码分析

来源:互联网 发布:观察宇宙的软件 编辑:程序博客网 时间:2024/05/30 04:15

最近本来想研究下spring security oauth2的配置,可是oauth2的配置是基于spring security的,没办法只能先搞懂spring security的配置了

先说下spring security的工作流程


以上是我自己总结的spring security的流程图 如有不正确的地方还请及时指教。

看完流程图之后我们再来分析下spring security的主要类与方法


org.springframework.security.webpublicclass FilterChainProxy extends GenericFilterBean public void doFilter(ServletRequest request, ServletResponse response ) throws IOException, ServletException{            if (currentPosition == size ) {                if (logger .isDebugEnabled()) {                    logger.debug(UrlUtils.buildRequestUrl( firewalledRequest)                            + " reached end of additional filter chain; proceeding with original chain");                }                // Deactivate path stripping as we exit the security filter chain                this.firewalledRequest .reset();                originalChain.doFilter(request , response );            } else {                 currentPosition++;                Filter nextFilter = additionalFilters .get(currentPosition - 1);                if (logger .isDebugEnabled()) {                    logger.debug(UrlUtils.buildRequestUrl( firewalledRequest) + " at position " + currentPosition + " of "                        + size + " in additional filter chain; firing Filter: '"                        + nextFilter.getClass().getSimpleName() + "'" );                }                nextFilter.doFilter(request , response , this);            }        }

该类是spring在web.xml中注册的org.springframework.web.filter.DelegatingFilterProxy对象中管理所有spring拦截器的一个拦截链,该类的主要方法是doFilter

我们可以看到拦截链中注册的所有spring的拦截器


其中第4个UsernamePasswordAuthenticationFilter是我注册用来自定义处理登录请求的拦截器
"LogoutFilter"用于拦截登出请求
"RequestCacheAwareFilter"用于记录这次请求的信息(看了下代码可能是的)
"AnonymousAuthenticationFilter"用于获取用户访问是的权限 如果没有权限则新建一个anymouse
"ExceptionTranslationFilter"用于拦截异常的拦截器 主要作用是拦截access_denied等异常
"FilterSecurityInterceptor"用于权限校验,其中AccessDecisionManager在这里对访问的路径和用户权限进行控制,一旦拒绝访问则抛出access_denied异常被“ExceptionTranslationFilter ”捕获进而被accessdeniedhandle处理
注意spring默认的用户登录处理拦截器:
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {    //~ Static fields/initializers =====================================================================================    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";    /**     * @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler}     */    @Deprecated    public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME" ;    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY ;    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY ;    private boolean postOnly = true;    //~ Constructors ===================================================================================================    public UsernamePasswordAuthenticationFilter() {        super("/j_spring_security_check" );    }

以上代码我们能看出spring-security的默认登录验证url为"/j_spring_security_check"而默认的登录名和密码为"j_username"和"j_password",这个在自己的登录页面要有相对应的命名,username和password我没有找到直接修改的办法(为此我才自己写了一个UsernamePasswordAuthenticationFilter用于自定义登录拦截的参数)而登录的url的话可以在<http>标签里面的login-processing-url进行配置

接下来看spring-security针对登录请求的拦截处理
UsenamePasswordAuthenticationFilter这个类含有一个ProviderManager用来获取用户信息
org.springframework.security.authenticationpublic class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBeanpublic Authentication authenticate(Authentication authentication) throws AuthenticationException//作为providerManager该类可提供多个provider对象用于获取用户对象        for (AuthenticationProvider provider : getProviders()) {            if (!provider .supports(toTest)) {                continue;            }            result = provider.authenticate( authentication);          }
在这里我们可以看到
org.springframework.security.authentication.dao
publicabstractclassAbstractUserDetailsAuthenticationProviderimplementsAuthenticationProvider, InitializingBean,MessageSourceAware 
publicAuthenticationauthenticate(Authentication authentication ) throwsAuthenticationException
该函数负责对登录的用户进行校验 其中authentication principal中存放了username,credentials 中存放了password

由于本人使用的是数据库存放用户信息所以spring-security使用这个类用于获取用户对象

org.springframework.security.authentication.daopublic class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider     protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)            throws AuthenticationException {        UserDetails loadedUser;        try {            loadedUser = this.getUserDetailsService().loadUserByUsername(username);        } catch (UsernameNotFoundException notFound) {            if(authentication.getCredentials() != null) {                String presentedPassword = authentication.getCredentials().toString();                passwordEncoder.isPasswordValid(userNotFoundEncodedPassword, presentedPassword, null);            }            throw notFound;        } catch (Exception repositoryProblem) {            throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);        }        if (loadedUser == null) {            throw new InternalAuthenticationServiceException(                    "UserDetailsService returned null, which is an interface contract violation");        }        return loadedUser;    }
该函数通过用户名获取数据库中的用户对象,用于与 authentication的password比较 还有进行权限的校验,注意这里的this.getUserDetailsService()应该又我们自己来提供,我们需要自己提供一个service来继承该下面的接口
package org.springframework.security.core.userdetails;public interface UserDetailsService {    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;}
比如我自己的:
package com.orimuse.core.spring.security.web.service;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import com.orimuse.core.spring.security.web.dao.UserDao;import com.orimuse.core.spring.security.web.entity.User;import com.orimuse.core.spring.security.web.util.OAuthorityProvider;import com.orimuse.core.web.dao.intf.BaseDao;import com.orimuse.core.web.service.impl.AbstractBaseService;@Servicepublic class AuthenticationService extends AbstractBaseService<User> implements UserDetailsService{private UserDao dao;@Overridepublic UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {User user = new User("user","passwd");user.setAuthorities(OAuthorityProvider.getAuthorityCollection(OAuthorityProvider.ROLE_USER));return user;}@Overridepublic BaseDao<User> getDao() {return this.dao;}}
loadUserByUsername返回一个UserDetails的实例,该实例中要包含用户的帐号密码以及用户的身份权限具体的UserDetails源码我也发下
package org.springframework.security.core.userdetails;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import java.io.Serializable;import java.util.Collection;/** * Provides core user information. * * <p> * Implementations are not used directly by Spring Security for security * purposes. They simply store user information which is later encapsulated * into {@link Authentication} objects. This allows non-security related user * information (such as email addresses, telephone numbers etc) to be stored * in a convenient location. * <p> * Concrete implementations must take particular care to ensure the non-null * contract detailed for each method is enforced. See * {@link org.springframework.security.core.userdetails.User} for a * reference implementation (which you might like to extend or use in your code). * * @see UserDetailsService * @see UserCache * * @author Ben Alex */public interface UserDetails extends Serializable {    //~ Methods ========================================================================================================    /**     * Returns the authorities granted to the user. Cannot return <code>null</code>.     *     * @return the authorities, sorted by natural key (never <code>null</code>)     */    Collection<? extends GrantedAuthority> getAuthorities();    /**     * Returns the password used to authenticate the user.     *     * @return the password     */    String getPassword();    /**     * Returns the username used to authenticate the user. Cannot return <code>null</code>.     *     * @return the username (never <code>null</code>)     */    String getUsername();    /**     * Indicates whether the user's account has expired. An expired account cannot be authenticated.     *     * @return <code>true</code> if the user's account is valid (ie non-expired), <code>false</code> if no longer valid     *         (ie expired)     */    boolean isAccountNonExpired();    /**     * Indicates whether the user is locked or unlocked. A locked user cannot be authenticated.     *     * @return <code>true</code> if the user is not locked, <code>false</code> otherwise     */    boolean isAccountNonLocked();    /**     * Indicates whether the user's credentials (password) has expired. Expired credentials prevent     * authentication.     *     * @return <code>true</code> if the user's credentials are valid (ie non-expired), <code>false</code> if no longer     *         valid (ie expired)     */    boolean isCredentialsNonExpired();    /**     * Indicates whether the user is enabled or disabled. A disabled user cannot be authenticated.     *     * @return <code>true</code> if the user is enabled, <code>false</code> otherwise     */    boolean isEnabled();}
只要实现该接口的对象都能返回

接下来就讲下我了解到的spring-security的几个基本的配置
        <security:http auto-config="true" disable-url-rewriting= "true">                      <security:intercept-url pattern= "/oauth/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />                      <security:intercept-url pattern= "/**" access ="ROLE_USER" />                      <security:custom-filter ref="userNamePasswordAuthenticationFilter" before="FORM_LOGIN_FILTER"/>                      <security:form-login login-page="/oauth/login" login-processing-url="/oauth/dologin" />        </security:http>
该标签是spring-security的基本标签,其作用是对AbstractAuthenticationProcessingFilter这个拦截器的一个初始化参数的作用, "auto-config"用于定义一些默认的配置,之前的说明文档中写道 起作用是对 <form-login>,<http-basic>,<logout>标签的缩写.
<intercept-url>这个标签是用来设定拦截规则,“pattern"为用于匹配该规则的url, “access”表示该规则的访问权限"IS_AUTHENTICATED_ANONYMOUSLY"表示"/oauth/login"该路径任何人都能访问(本例中,该拦截规则的意思是 对于登录页面"/oauth/login"任何人都可以访问)
<security:intercept-url pattern= "/**" access ="ROLE_USER" />
该拦截规则意思是对所有的路径都要有"role_user"这个身份的人才能进入(但是登录页面不需要)
<security:custom-filter ref="userNamePasswordAuthenticationFilter" before="FORM_LOGIN_FILTER"/>
用来自定义拦截器的标签,我们可以重写一个登录请求的拦截器。before表示该拦截器的顺序,before="FORM_LOGIN_FILTER"的意思应该是放在默认的登录请求拦截器之前保证我们自定义的拦截器能够生效而请求不会被默认的拦截器拦截.
< security:http pattern ="/css/**" security ="none"/>
该标签也是一个拦截规则的设定标签,但作用是用来取消对某些资源访问的拦截控制,比如js和css等静态资源如果不需要权限验证的话就可以加上该规则,注意一定要加在<security:httpauto-config="true"disable-url-rewriting="true">的前面否则启动时会报错(可能是与拦截顺序有关,具体原因不清楚)
        <security:authentication-manager>               <security:authentication-provider user-service-ref='userDetailsService' >               </security:authentication-provider>        </security:authentication-manager>
该标签是定义一个providerManager对象,用于管理provider们.目前我已知的provider作用就是提供一个获取用户信息的接口用于身份验证,也就是上面我自己写的AuthenticationService









0 0
原创粉丝点击