`
mengqingyu
  • 浏览: 328630 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

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 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 String							authoritiesByUsernameQuery;

	private String							usersByUsernameQuery;

	//代码略.....................
	
	public void setAuthoritiesByUsernameQuery(String queryString) {
		authoritiesByUsernameQuery = queryString;
	}
	
	public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
		this.usersByUsernameQuery = usersByUsernameQueryString;
	}
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
       List 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 dbAuthsSet = new HashSet();

        if (enableAuthorities) {
            dbAuthsSet.addAll(loadUserAuthorities(user.getUsername())); //查询用户拥有的角色
        }

        if (enableGroups) {
            dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));
        }

        List dbAuths = new ArrayList(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 loadUsersByUsername(String username) {
		return getJdbcTemplate().query(usersByUsernameQuery, new String[]{
			username}, new RowMapper() {

			@Override
			public 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 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 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 attributes) {
        int result = ACCESS_ABSTAIN;
        Collection 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 extractAuthorities(Authentication authentication) {
        return authentication.getAuthorities();
    }
	
	//代码略.....................
}


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics