美文网首页程序员Java
Spring Security 源码之 Filter Part

Spring Security 源码之 Filter Part

作者: AlienPaul | 来源:发表于2020-11-20 16:41 被阅读0次

AnonymousAuthenticationFilter

用于实现匿名登陆。如果SecurityContext没有写入认证信息,该filter会在其中写入一个AnonymousAuthenticationToken

它的逻辑比较简单,如下所示:

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
    // 如果SecurityContext没有保存认证信息
    if (SecurityContextHolder.getContext().getAuthentication() == null) {
        // 在SecurityContext设置一个AnonymousAuthenticationToken
        SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
                    + SecurityContextHolder.getContext().getAuthentication()));
        }
        else {
            this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
        }
    }
    else {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(LogMessage.of(() -> "Did not set SecurityContextHolder since already authenticated "
                    + SecurityContextHolder.getContext().getAuthentication()));
        }
    }
    chain.doFilter(req, res);
}

SessionManagementFilter

如果有新用户认证通过,调用session相关的操作,即SessionAuthenticationStrategy。例如激活session-fixation保护机制或检测用户多次登录。

有关SessionAuthenticationStrategy的详细分析请参考Spring Security 源码之 SessionAuthenticationStrategy

代码如下所示:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    // 确保filter只执行一次
    if (request.getAttribute(FILTER_APPLIED) != null) {
        chain.doFilter(request, response);
        return;
    }

    request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

    // 如果是新认证的用户securityContextRepository还没有保存SecurityContext
    if (!securityContextRepository.containsContext(request)) {
        // 获取SecurityContextHolder已保存的authentication
        Authentication authentication = SecurityContextHolder.getContext()
                .getAuthentication();
        // 如果用户已登录,且不是匿名登录
        if (authentication != null && !trustResolver.isAnonymous(authentication)) {
            // The user has been authenticated during the current request, so call the
            // session strategy
            try {
                // 调用sessionStrategy的认证成功相关逻辑
                sessionAuthenticationStrategy.onAuthentication(authentication,
                        request, response);
            }
            catch (SessionAuthenticationException e) {
                // The session strategy can reject the authentication
                logger.debug(
                        "SessionAuthenticationStrategy rejected the authentication object",
                        e);
                // 如果遇到错误,清空SecurityContext,调用认证失败逻辑
                SecurityContextHolder.clearContext();
                failureHandler.onAuthenticationFailure(request, response, e);

                return;
            }
            // Eagerly save the security context to make it available for any possible
            // re-entrant
            // requests which may occur before the current request completes.
            // SEC-1396.
            // 储存securityContext到repository
            securityContextRepository.saveContext(SecurityContextHolder.getContext(),
                    request, response);
        }
        else {
            // No security context or authentication present. Check for a session
            // timeout
            // 如果session已失效
            if (request.getRequestedSessionId() != null
                    && !request.isRequestedSessionIdValid()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Requested session ID "
                            + request.getRequestedSessionId() + " is invalid.");
                }
                // 调用session失效逻辑,跳转页面到指定URL
                if (invalidSessionStrategy != null) {
                    invalidSessionStrategy
                            .onInvalidSessionDetected(request, response);
                    return;
                }
            }
        }
    }

    chain.doFilter(request, response);
}

注意:本人在阅读此处代码时有个疑问,AbstractAuthenticationProcessingFilter中也调用了SessionAuthenticationStrategyonauthentication方法,感觉和SessionManagementFilter的逻辑有重复。AbstractAuthenticationProcessingFilter相关代码如下所示:

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    // ...
    }
    try {
        // ...
        // 注意,这里也调用了sessionStrategy的onAuthentication方法
        this.sessionStrategy.onAuthentication(authenticationResult, request, response);
        // Authentication success
        if (this.continueChainBeforeSuccessfulAuthentication) {
            chain.doFilter(request, response);
        }
        successfulAuthentication(request, response, chain, authenticationResult);
    }
    // ...
}

我们注意到上面的代码中有一个变量continueChainBeforeSuccessfulAuthentication,如果这个变量为false,不会再调用chain.doFilter,即后面的filter不会再调用。SessionManagementFilter位于AbstractAuthenticationProcessingFilter,自然不会被调用。变量continueChainBeforeSuccessfulAuthentication源码中的解释如下:

/**
 * Indicates if the filter chain should be continued prior to delegation to
 * {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, Authentication)}
 * , which may be useful in certain environment (such as Tapestry applications).
 * Defaults to <code>false</code>.
 */

这个变量表示认证成功后,调用successfulAuthentication方法前是否调用后面的filter链,在Tapestry应用中可能会用到(我也不清楚这个是什么)。但是它的默认值为false,不会调用后面的filter。

本文为原创内容,欢迎大家讨论、批评指正与转载。转载时请注明出处。

相关文章

网友评论

    本文标题:Spring Security 源码之 Filter Part

    本文链接:https://www.haomeiwen.com/subject/cnzdiktx.html