读书人

spring-security3(2)源码分析

发布时间: 2012-06-26 10:04:13 作者: rapoo

spring-security3(二)源码分析
利用断点走了一遍spring-security源码的核心部分,下面根据自己的理解对源码做了一些解释,过滤器开头的标号是运行时默认配置调用的顺序,理解了原理,我们可以通过继承和实现接口的方式扩展过滤器,权限验证器,数据查询器,投票器等等......

1.SecurityContextPersistenceFilter 从HttpSession中获取SecurityContext上下文
2.logoutFilter 如果访问地址为/j_spring_security_logout,LogoutFilter将注销用户
3.AbstractAuthenticationProcessingFilter 权限管理器如果访问地址为/j_spring_security_check则选择对应的数据查询器来获取存储的用户相关信息
4.BasicAuthenticationFilter
5.RequestCacheAwareFilter
6.SecurityContextHolderAwareRequestFilter
7.RememberMeAuthenticationFilter 如果当前SecurityContextHolder中没有用户对象,则通过cookie查找
8.AnonymousAuthenticationFilter 如果当前SecurityContextHolder中没有用户对象,则创建匿名对象
9.SessionManagementFilter 检查session是否超时
10.ExceptionTranslationFilter 调用FilterSecurityInterceptor,AbstractSecurityInterceptor使用投票器进行权限判断
11.SwitchUserFilter 用户切换高权限用户向低权限用户切换

//从HttpSession中获取SecurityContext上下文 public class SecurityContextPersistenceFilter extends GenericFilterBean {    private SecurityContextRepository repo = new HttpSessionSecurityContextRepository();    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)            throws IOException, ServletException {        //代码略.....................        HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);        SecurityContext contextBeforeChainExecution = repo.loadContext(holder);//获得security上下文        try {            SecurityContextHolder.setContext(contextBeforeChainExecution);            chain.doFilter(holder.getRequest(), holder.getResponse());//调用下一个过滤器        } finally {            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();            // Crucial removal of SecurityContextHolder contents - do this before anything else.            SecurityContextHolder.clearContext();            repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());            request.removeAttribute(FILTER_APPLIED);        }    }//代码略.....................}//如果访问地址为/j_spring_security_logout,LogoutFilter将注销用户public class LogoutFilter extends GenericFilterBean {    private String filterProcessesUrl = "/j_spring_security_logout";     //代码略.....................    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)            throws IOException, ServletException {//判断如果访问地址为/j_spring_security_logout则执行注销,否则跳过        if (requiresLogout(request, response)) {             Authentication auth = SecurityContextHolder.getContext().getAuthentication();            for (LogoutHandler handler : handlers) {                handler.logout(request, response, auth);            }            logoutSuccessHandler.onLogoutSuccess(request, response, auth);            return;        }        chain.doFilter(request, response);    }    //代码略.....................}//权限管理器如果访问地址为/j_spring_security_check则选择对应的数据查询器来获取存储的用户相关信息public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements    ApplicationEventPublisherAware, MessageSourceAware {    //代码略.....................public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {//判断如果访问地址为/j_spring_security_check则跳过进行权限获取和判断,否则执行AnonymousAuthenticationFilter        if (!requiresAuthentication(request, response)) {             chain.doFilter(request, response); //调用下一个过滤器BasicAuthenticationFilter            return;        }        Authentication authResult;        try {//执行UsernamePasswordAuthenticationFilter类中的方法            authResult = attemptAuthentication(request, response);            if (authResult == null) {                // return immediately as subclass has indicated that it hasn't completed authentication                return;            }            sessionStrategy.onAuthentication(authResult, request, response);        }        catch (AuthenticationException failed) {            // Authentication failed            unsuccessfulAuthentication(request, response, failed);            return;        }        // Authentication success        if (continueChainBeforeSuccessfulAuthentication) {            chain.doFilter(request, response);        }        successfulAuthentication(request, response, authResult);}protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,Authentication authResult) throws IOException, ServletException {        SecurityContextHolder.getContext().setAuthentication(authResult);        rememberMeServices.loginSuccess(request, response, authResult); //是否存入cookie        // Fire event        if (this.eventPublisher != null) {            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));        }        successHandler.onAuthenticationSuccess(request, response, authResult);//跳转到目标页面    }//代码略.....................}public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {    //代码略.....................    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {        if (postOnly && !request.getMethod().equals("POST")) {            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());        }//获取页面提交的用户名、密码        String username = obtainUsername(request);        String password = obtainPassword(request);        username = username.trim();//封装成token对象        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);        //代码略.....................//调用AbstractAuthenticationManager类中的方法        return this.getAuthenticationManager().authenticate(authRequest);    }//代码略.....................}public abstract class AbstractAuthenticationManager implements AuthenticationManager {//代码略.....................    public final Authentication authenticate(Authentication authRequest) throws AuthenticationException {        try {            return doAuthentication(authRequest);//调用ProviderManager类中的方法        } catch (AuthenticationException e) {            e.setAuthentication(authRequest);            if (clearExtraInformation) {                e.clearExtraInformation();            }            throw e;        }    }//代码略.....................}//权限认证管理器public class ProviderManager extends AbstractAuthenticationManager implements MessageSourceAware, InitializingBean {private List<AuthenticationProvider> providers = Collections.emptyList();private AuthenticationManager parent;//代码略.....................    public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {        Class<? extends Authentication> toTest = authentication.getClass();        AuthenticationException lastException = null;        Authentication result = null;        for (AuthenticationProvider provider : getProviders()) {            if (!provider.supports(toTest)) {                continue;            }            try {//调用AbstractUserDetailsAuthenticationProvider类中的方法                result = provider.authenticate(authentication);                if (result != null) {                    copyDetails(authentication, result);                    break;                }            } catch (AccountStatusException e) {                // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status                eventPublisher.publishAuthenticationFailure(e, authentication);                throw e;            } catch (AuthenticationException e) {                lastException = e;            }        }        if (result == null && parent != null) {            // Allow the parent to try.            try {                result = parent.authenticate(authentication);            } catch (ProviderNotFoundException e) {                // ignore as we will throw below if no other exception occurred prior to calling parent and the parent                // may throw ProviderNotFound even though a provider in the child already handled the request            } catch (AuthenticationException e) {                lastException = e;            }        }        if (result != null) {            if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {                // Authentication is complete. Remove credentials and other secret data from authentication                ((CredentialsContainer)result).eraseCredentials();            }            eventPublisher.publishAuthenticationSuccess(result);            return result;        }        eventPublisher.publishAuthenticationFailure(lastException, authentication);        throw lastException;    }//代码略.....................}//权限查询器public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean,    MessageSourceAware {//代码略.....................    public Authentication authenticate(Authentication authentication) throws AuthenticationException {        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,            messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",                "Only UsernamePasswordAuthenticationToken is supported"));        // Determine username        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();        boolean cacheWasUsed = true;        UserDetails user = this.userCache.getUserFromCache(username); //从Ehcache实现的缓存里取userDetail对象        if (user == null) {            cacheWasUsed = false;            try {//调用DaoAuthenticationProvider类中的方法                user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);            } catch (UsernameNotFoundException notFound) {                logger.debug("User '" + username + "' not found");                if (hideUserNotFoundExceptions) {                    throw new BadCredentialsException(messages.getMessage(                            "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));                } else {                    throw notFound;                }            }            Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");        }        try {            preAuthenticationChecks.check(user); //检查用户是否有效//通过页面传入的用户名、密码和数据库中取出的信息对比验证            additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);         } catch (AuthenticationException exception) {            if (cacheWasUsed) {                // There was a problem, so try again after checking                // we're using latest data (i.e. not from the cache)                cacheWasUsed = false;                user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);                preAuthenticationChecks.check(user);                additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);            } else {                throw exception;            }        }        postAuthenticationChecks.check(user);        if (!cacheWasUsed) {            this.userCache.putUserInCache(user); //将用户对象放入缓存        }        Object principalToReturn = user;        if (forcePrincipalAsString) {            principalToReturn = user.getUsername();        }        return createSuccessAuthentication(principalToReturn, authentication, user);    }//代码略.....................}//数据库查询器class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{private UserDetailsService userDetailsService;//代码略.....................    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)            throws AuthenticationException {        UserDetails loadedUser;        try {//获取用户信息,可以通过实现UserDetailsService接口或继承JdbcDaoImpl类来自定义内部实现            loadedUser = this.getUserDetailsService().loadUserByUsername(username);//调用自定义类UserDetailsServiceImpl的方法        }        catch (DataAccessException repositoryProblem) {            throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);        }        if (loadedUser == null) {            throw new AuthenticationServiceException(                    "UserDetailsService returned null, which is an interface contract violation");        }        return loadedUser;    }protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {        Object salt = null;        if (this.saltSource != null) {            salt = this.saltSource.getSalt(userDetails);        }        if (authentication.getCredentials() == null) {            logger.debug("Authentication failed: no credentials provided");            throw new BadCredentialsException(messages.getMessage(                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),                    includeDetailsObject ? userDetails : null);        }        String presentedPassword = authentication.getCredentials().toString();//判断密码是否一致        if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {            logger.debug("Authentication failed: password does not match stored value");            throw new BadCredentialsException(messages.getMessage(                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),                    includeDetailsObject ? userDetails : null);        }    }//代码略.....................}//自定义类实现查询接口public class UserDetailsServiceImpl extends JdbcDaoSupport implements UserDetailsService {private StringauthoritiesByUsernameQuery;private StringusersByUsernameQuery;//代码略.....................public void setAuthoritiesByUsernameQuery(String queryString) {authoritiesByUsernameQuery = queryString;}public void setUsersByUsernameQuery(String usersByUsernameQueryString) {this.usersByUsernameQuery = usersByUsernameQueryString;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {       List<UserDetails> users = loadUsersByUsername(username); //访问数据库查询用户信息        if (users.size() == 0) {            logger.debug("Query returned no results for user '" + username + "'");            throw new UsernameNotFoundException(                    messages.getMessage("JdbcDaoImpl.notFound", new Object[]{username}, "Username {0} not found"), username);        }        UserDetails user = users.get(0); // contains no GrantedAuthority[]        Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>();        if (enableAuthorities) {            dbAuthsSet.addAll(loadUserAuthorities(user.getUsername())); //查询用户拥有的角色        }        if (enableGroups) {            dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));        }        List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet);        addCustomAuthorities(user.getUsername(), dbAuths);        if (dbAuths.size() == 0) {            logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'");            throw new UsernameNotFoundException(                    messages.getMessage("JdbcDaoImpl.noAuthority",                            new Object[] {username}, "User {0} has no GrantedAuthority"), username);        }        return createUserDetails(username, user, dbAuths);//返回实现UserDetails接口的对象,将验证信息封装到此对象中}protected List<UserDetails> loadUsersByUsername(String username) {return getJdbcTemplate().query(usersByUsernameQuery, new String[]{username}, new RowMapper<UserDetails>() {@Overridepublic UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {String username = rs.getString(1);String password = rs.getString(2);boolean enabled = rs.getBoolean(3);return new userDetailsImpl(username, password, enabled);//返回实现UserDetails接口的对象}});}//代码略.....................}//用户信息bean,必须实现UserDetails接口public class userDetailsImpl implements UserDetails {//代码略.....................}//由ExceptionTranslationFilter过滤器调用来进行权限判断public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware,    MessageSourceAware {//代码略.....................    protected InterceptorStatusToken beforeInvocation(Object object) {//这里读取配置FilterSecurityInterceptor的SecurityMetadataSource属性来获取配置的角色,这些属性配置了资源的安全设置        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);        if (attributes == null) {            if (rejectPublicInvocations) {                throw new IllegalArgumentException("Secure object invocation " + object +                        " was denied as public invocations are not allowed via this interceptor. "                                + "This indicates a configuration error because the "                                + "rejectPublicInvocations property is set to 'true'");            }            publishEvent(new PublicInvocationEvent(object));            return null; // no further work post-invocation        }//这里从SecurityContextHolder中去取Authentication对象,一般在登录时会放到SecurityContextHolder中去         if (SecurityContextHolder.getContext().getAuthentication() == null) {            credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",                    "An Authentication object was not found in the SecurityContext"), object, attributes);        }// 如果前面没有处理鉴权,这里需要对鉴权进行处理        Authentication authenticated = authenticateIfRequired();        // Attempt authorization        try {//通过投票器判断当前角色是否有权限访问该地址,如果没有权限则抛出异常,调用AffirmativeBased类中的decide的方法            this.accessDecisionManager.decide(authenticated, object, attributes);        }        catch (AccessDeniedException accessDeniedException) {//授权不成功向外发布事件            publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,                    accessDeniedException));            throw accessDeniedException;        }        publishEvent(new AuthorizedEvent(object, attributes, authenticated));        // 这里构建一个RunAsManager来替代当前的Authentication对象,默认情况下使用的是NullRunAsManager会把SecurityContextHolder中的Authentication对象清空        Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);        if (runAs == null) {            // no further work post-invocation            return new InterceptorStatusToken(authenticated, false, attributes, object);        } else {            SecurityContextHolder.getContext().setAuthentication(runAs);            // need to revert to token.Authenticated post-invocation            return new InterceptorStatusToken(authenticated, true, attributes, object);        }    }//代码略.....................}//决策器public class AffirmativeBased extends AbstractAccessDecisionManager {//代码略.....................    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)        throws AccessDeniedException {        int deny = 0;//依次使用各个投票器进行投票,并对投票结果进行计票         for (AccessDecisionVoter voter : getDecisionVoters()) {            int result = voter.vote(authentication, object, configAttributes);            if (logger.isDebugEnabled()) {                logger.debug("Voter: " + voter + ", returned: " + result);            }//这是对投票结果进行处理,如果遇到其中一票通过,那就授权通过,如果是弃权或者反对,那就继续投票            switch (result) {            case AccessDecisionVoter.ACCESS_GRANTED: //result:1                return;            case AccessDecisionVoter.ACCESS_DENIED: //result:-1    //这里对反对票进行计数                 deny++;                break;            default:                break;            }        }//如果有反对票,抛出异常,整个授权不通过        if (deny > 0) {            throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",                    "Access is denied"));        }        //这里对弃权票进行处理,看看是全是弃权票的决定情况,默认是不通过,由allowIfAllAbstainDecisions变量控制        checkAllowIfAllAbstainDecisions();    }//代码略.....................}//角色投票器public class RoleVoter implements AccessDecisionVoter { //代码略.....................    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {        int result = ACCESS_ABSTAIN;        Collection<GrantedAuthority> authorities = extractAuthorities(authentication);        for (ConfigAttribute attribute : attributes) {//这里取得资源的安全配置              if (this.supports(attribute)) {                 result = ACCESS_DENIED;                //这里对资源配置的安全授权级别进行判断,也就是匹配ROLE为前缀的角色配置                   //遍历每个配置属性,如果其中一个匹配该主体持有的GrantedAuthority,则访问被允许。//当前用户拥有的角色集合,当有任何一个角色满足时授予权限                for (GrantedAuthority authority : authorities) {                     if (attribute.getAttribute().equals(authority.getAuthority())) {                        return ACCESS_GRANTED;                    }                }            }        }        return result;    }    Collection<GrantedAuthority> extractAuthorities(Authentication authentication) {        return authentication.getAuthorities();    }//代码略.....................}


读书人网 >软件架构设计

热点推荐