Spring Security - 图解认证管理器AuthenticationManager

1. 前言

我们上一篇介绍了 UsernamePasswordAuthenticationFilter 的工作流程,留下了一个小小的伏笔,作为一个 Servlet Filter 应该存在一个 doFilter 实现方法,而它却没有,其实它的父类 AbstractAuthenticationProcessingFilter 提供了具体的实现。稍后我们会根据这个实现引出今天的主角 AuthenticationManager ,来继续介绍用户的认证过程。

2. AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter 作为 UsernamePasswordAuthenticationFilter 的父类,实现了认证过滤器的处理逻辑。我们来看看它的核心方法 doFilter 的实现:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {

   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;

    // 先通过请求的uri来判断是否需要认证,比如默认的/login 
   if (!requiresAuthentication(request, response)) {
      chain.doFilter(request, response);

      return;
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Request is to process authentication");
   }

   Authentication authResult;

   try {
       // 接着就是执行子类钩子方法attemptAuthentication来获取认证结果对象Authentication ,这个对象不能是空 否则直接返回
      authResult = attemptAuthentication(request, response);
      if (authResult == null) {
         // return immediately as subclass has indicated that it hasn't completed
         // authentication
         return;
      }
       // 处理session 策略,这里默认没有任何策略
      sessionStrategy.onAuthentication(authResult, request, response);
   }
   catch (InternalAuthenticationServiceException failed) {
      logger.error(
            "An internal error occurred while trying to authenticate the user.",
            failed);
       // 如果遇到异常 就会交给认证失败处理器 AuthenticationFailureHandler 来处理
      unsuccessfulAuthentication(request, response, failed);

      return;
   }
   catch (AuthenticationException failed) {
      // Authentication failed
      unsuccessfulAuthentication(request, response, failed);

      return;
   }

   //  认证成功后继续其它过滤器链 并最终交给认证成功处理器 AuthenticationSuccessHandler 处理
   if (continueChainBeforeSuccessfulAuthentication) {
      chain.doFilter(request, response);
   }

   successfulAuthentication(request, response, chain, authResult);
}

大部分逻辑这里是清晰的,关键在于 attemptAuthentication 方法,这个我们已经在上一文分析了是通过 AuthenticationManagerauthenticate 方法进行认证逻辑的处理,接下来我们将重点分析这个接口来帮助我们了解 Spring Seucirty 的认证过程。

3. AuthenticationManager

AuthenticationManager 这个接口方法非常奇特,入参和返回值的类型都是 Authentication 。该接口的作用是对用户的未授信凭据进行认证,认证通过则返回授信状态的凭据,否则将抛出认证异常 AuthenticationException

3.1 AuthenticationManager的初始化流程

那么 AbstractAuthenticationProcessingFilter 中的 AuthenticationManager 是在哪里配置的呢? 看过Spring Security 实战干货系列应该知道 WebSecurityConfigurerAdapter 中的 void configure(AuthenticationManagerBuilder auth) 是配置 AuthenticationManager 的地方, 我根据源码总结了一下 AuthenticationManager 的初始化流程,相信可以帮助你去阅读相关的源码:

AuthenticationManager的初始化流程

需要注意的是如果我们使用自定义配置一定不能按照类似下面的错误示范:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
    daoAuthenticationProvider.setUserDetailsService(weChatSecurityConfigProperties.getUserDetailsService());
    daoAuthenticationProvider.setPasswordEncoder(multiPasswordEncoder());
    auth.authenticationProvider(daoAuthenticationProvider);
    // 调用 super 将导致不生效 所以下面语句不要写
    super.configure(auth);
}

3.2 AuthenticationManager的认证过程

AuthenticationManager 的实现 ProviderManager 管理了众多的 AuthenticationProvider 。每一个 AuthenticationProvider 都只支持特定类型的 Authentication ,然后是对适配到的 Authentication 进行认证,只要有一个 AuthenticationProvider 认证成功,那么就认为认证成功,所有的都没有通过才认为是认证失败。认证成功后的 Authentication 就变成授信凭据,并触发认证成功的事件。认证失败的就抛出异常触发认证失败的事件。

ProviderManager

从这里我们可以看出认证管理器 AuthenticationManager 针对特定的 Authentication 提供了特定的认证功能,我们可以借此来实现多种认证并存。

4. 总结

通过本文我们对 Spring Security 认证管理器 AuthenticationManager 的初始化过程和认证过程进行了分析,如果你熟悉了 AuthenticationManager 的逻辑可以实现多种认证方式的并存等能力,实现很多有用的逻辑,这对集成 Spring Security 到项目中非常重要。多多关注: 码农小胖哥 获取更多的原创干货。


原文:Spring Security 实战干货:图解认证管理器AuthenticationManager | Java|Spring Security|Spring Boot|Spring Cloud|https://felord.cn 码农小胖哥的博客