07、Spring Security 实战 - 认证入口:AbstractAuthenticationProcessingFilter 过滤器

AbstractAuthenticationProcessingFilter 的职责也就非常明确:处理所有HTTP Request和Response对象,并将其封装成AuthenticationMananger可以处理的Authentication。并且在身份验证成功或失败之后将对应的行为转换为HTTP的Response,同时还要处理一些Web特有的资源比如Session和Cookie。

AbstractAuthenticationProcessingFilter 源码:

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
   
     
    
    //事件发布管理器
    protected ApplicationEventPublisher eventPublisher;
    protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
    // 认证管理器,定义了SpringSecurity如何进行认证操作
    // 认证成功后会返回一个Authentication对象,这个对象会被设置到SecurityContextHolder中
    private AuthenticationManager authenticationManager;
    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    // 如果用户开启了类似“记住我”之类的免密码登录,RememberMeServices来进行管理。
    private RememberMeServices rememberMeServices = new NullRememberMeServices();
    // 请求匹配器,定义了match()方法,匹配请求HttpServletRequest是否符合定义的规则
    private RequestMatcher requiresAuthenticationRequestMatcher;
    private boolean continueChainBeforeSuccessfulAuthentication = false;
    // 会话验证管理
    private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
    private boolean allowSessionCreation = true;
    // 用户登录成功的后续处理
    private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    // 用户登录失败的后续处理
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
    
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
   
     
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        // 判断是否需要认证
        if (!this.requiresAuthentication(request, response)) {
   
     
            // 如果不需要认证,继续执行下一个过滤器
            chain.doFilter(request, response);
        } else {
   
     
            Authentication authResult;
            try {
   
     
                // 认证处理,该方法需要子类去重写
                authResult = this.attemptAuthentication(request, response);
                if (authResult == null) {
   
     
                    return;
                }
                // 身份认证成功,保存session
                this.sessionStrategy.onAuthentication(authResult, request, response);
            } catch (InternalAuthenticationServiceException var8) {
   
     
                this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
                // 认证失败的处理逻辑
                this.unsuccessfulAuthentication(request, response, var8);
                return;
            } catch (AuthenticationException var9) {
   
     
                // 认证失败的处理逻辑
                this.unsuccessfulAuthentication(request, response, var9);
                return;
            }
            if (this.continueChainBeforeSuccessfulAuthentication) {
   
     
                chain.doFilter(request, response);
            }
            // 认证成功的处理逻辑
            this.successfulAuthentication(request, response, chain, authResult);
        }
    }

     // 判断该filter是否需要处理该次请求,即请求的路径和该filter配置的要处理的url是否匹配
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
   
     
        return this.requiresAuthenticationRequestMatcher.matches(request);
    }

    // 需要子类提供身份认证的具体实现。
    public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;

    // 认证成功的处理逻辑
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
   
     
        // 将认证成功的用户信息保存到 SecurityContextHolder
        SecurityContextHolder.getContext().setAuthentication(authResult);
        // 处理记住我逻辑
        this.rememberMeServices.loginSuccess(request, response, authResult);
        // 发布时间,即发布认证成功消息,供其他的bean接收和处理
        if (this.eventPublisher != null) {
   
     
            this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
// 认证成功的处理逻辑
            this.successfulAuthentication(request, response, chain, authResult);
        }
    }

     // 判断该filter是否需要处理该次请求,即请求的路径和该filter配置的要处理的url是否匹配
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
   
     
        return this.requiresAuthenticationRequestMatcher.matches(request);
    }

    // 需要子类提供身份认证的具体实现。
    public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;

    // 认证成功的处理逻辑
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
   
     
        // 将认证成功的用户信息保存到 SecurityContextHolder
        SecurityContextHolder.getContext().setAuthentication(authResult);
        // 处理记住我逻辑
        this.rememberMeServices.loginSuccess(request, response, authResult);
        // 发布时间,即发布认证成功消息,供其他的bean接收和处理
        if (this.eventPublisher != null) {
   
     
            this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
		// 认证成功后后续处理
        this.successHandler.onAuthenticationSuccess(request, response, authResult);
    }

    // 认证失败的处理逻辑
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
   
     
        // 清除 SecurityContextHolder 存储的认证信息
        SecurityContextHolder.clearContext();
        // 清除 rememberMe 存储的认证信息
        this.rememberMeServices.loginFail(request, response);
        // 认证失败的后续处理
        this.failureHandler.onAuthenticationFailure(request, response, failed);
    }
}    

AbstractAuthenticationProcessingFilter为了完成与浏览器和HTTP请求的验证,它将大任务拆成了几个子任务并交给了以下组件完成:

  • AuthenticationManager用于处理身份验证的核心逻辑;
  • AuthenticationSuccessHandler用于处理验证成功的后续流程;
  • AuthenticationFailureHandler用于处理失败的后续流程;
  • 在验证成功后发布一个名为InteractiveAuthenticationSuccessEvent的事件通知给到应用上下文,用于告知身份验证已经成功;
    因为是基于浏览器所以相关的会话管理行为交由 SessionAuthenticationStrategy来进行实现。
  • 如果用户开启了类似“记住我”之类的免密码登录,AbstractAuthenticationProcessingFilter还有一个名为RememberMeServices来进行管理。

AbstractAuthenticationProcessingFilte 作为身份认证请求入口,是一个抽象类。OAuth2ClientAuthenticationProcessingFilter(Spriing OAuth2)、RememberMeAuthenticationFilter(RememberMe)都继承了 AbstractAuthenticationProcessingFilter ,并重写了方法 attemptAuthentication 进行身份认证。

 
UsernamePasswordAuthenticationFilter 过滤器:SpringSecurity 中默认的是表单登录格式,即用户在表单中输入用户名和密码进行登录,登录参数的提取在 UsernamePasswordAuthenticationFilter 过滤器中完成,UsernamePasswordAuthenticationFilter 是AbstractAuthenticationProcessingFilter 针对使用用户名和密码进行身份认证而定制化的一个过滤器。其添加是在调用http.formLogin() 时作用,默认的登录请求 url 为 “/login”,并且为 POST 请求。当我们登录的时候,也就是匹配到loginProcessingUrl,这个过滤器就会委托认证管理器 authenticationManager 来验证登录。

ClientCredentialsTokenEndpointFilter 过滤器:Spring Security对于获取TOKEN的请求(默认是"/oauth/token"),需要认证client_id和client_secret。认证client_id和client_secret可以有2种方式,一种是通过本节讲的ClientCredentialsTokenEndpointFilter,另一种是通过BasicAuthenticationFilter。ClientCredentialsTokenEndpointFilter首先比对请求URL是否是TOKEN请求路径以及请求参数中是否包含client_id,如果满足以上条件,再调用ProviderManager认证client_id和client_secret是否与配置的一致。如果通过认证,会把身份认证信息保存打SecurityContext上下文中。

OAuth2AuthenticationProcessingFilter 过滤器:授权认证服务通过认证后会返回 Access Token,该token可用于请求资源服务(业务系统)的接口。我们需要把特定的信息放到请求头中,例如在请求头中写入Authorization: Bearer !xBYUEBY0N3o234N,Authorization为key,Bearer !xBYUEBY0N3o234N为value,!xBYUEBY0N3o234N是Access Token。请求经过OAuth2AuthenticationProcessingFilter后,过滤器中的认证管理器会调用配置的授权认证服务的check token接口校验token是否有效,token有效则可以继续访问了。需要注意的是,对于资源认证服务Spring Security会把这个过滤器加入到过滤链里,但授权认证认证服务却不能。