• • AuthenticationConfiguration是认证管理器的配置类,当没有重写下面方法的时候会通过此配置类构建全局认证管理器
public abstract class WebSecurityConfigurerAdapter implements
    ......
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       this.disableLocalConfigureAuthenticationBldr = true;
    }
    ......
}

1、重要方法

1.1 authenticationManagerBuilder()

  • • 作用是往容器中注册一个全局认证管理器构建器
@Configuration(proxyBeanMethods = false)
// 导入了AutowireBeanFactoryObjectPostProcessor
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
    ......
    @Bean
    public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
          ApplicationContext context) {
       //是一个懒加载机制的,只有用到才会真正创建
       LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
       //获取认证事件推送器
       AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context,
             AuthenticationEventPublisher.class);
       DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
             objectPostProcessor, defaultPasswordEncoder);
       if (authenticationEventPublisher != null) {
          result.authenticationEventPublisher(authenticationEventPublisher);
       }
       return result;
    }
    ......
}

1.1.2 LazyPasswordEncoder

  • • LazyPasswordEncoder是SpringSecurity创建的全局认证管理器的时候,创建的懒加载机制的密码编码器
  • • 可以看出不管是匹配密码还是更新密码都是从容器获取PasswordEncoder达到一个懒加载的作用
static class LazyPasswordEncoder implements PasswordEncoder {

   private ApplicationContext applicationContext;

   private PasswordEncoder passwordEncoder;

   LazyPasswordEncoder(ApplicationContext applicationContext) {
      this.applicationContext = applicationContext;
   }

   @Override
   public String encode(CharSequence rawPassword) {
      return getPasswordEncoder().encode(rawPassword);
   }

   @Override
   public boolean matches(CharSequence rawPassword, String encodedPassword) {
      return getPasswordEncoder().matches(rawPassword, encodedPassword);
   }

   @Override
   public boolean upgradeEncoding(String encodedPassword) {
      return getPasswordEncoder().upgradeEncoding(encodedPassword);
   }

   private PasswordEncoder getPasswordEncoder() {
      if (this.passwordEncoder != null) {
         return this.passwordEncoder;
      }
      PasswordEncoder passwordEncoder = getBeanOrNull(this.applicationContext, PasswordEncoder.class);
      if (passwordEncoder == null) {
         passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
      }
      this.passwordEncoder = passwordEncoder;
      return passwordEncoder;
   }

}

1.2 三大配置类

  • • 这里的配置类指的是全局认证管理器构建器的配置类
  • • 但实际上这些配置类本身没什么作用,重点是又导入了两个配置类
  • • InitializeUserDetailsManagerConfigurer
  • • InitializeAuthenticationProviderManagerConfigurer
public class AuthenticationConfiguration {
    ......
    @Bean
    public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
          ApplicationContext context) {
       return new EnableGlobalAuthenticationAutowiredConfigurer(context);
    }

    @Bean
    public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(
          ApplicationContext context) {
       return new InitializeUserDetailsBeanManagerConfigurer(context);
    }

    @Bean
    public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(
          ApplicationContext context) {
       return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
    }
    ......
}

1.2.1 InitializeUserDetailsManagerConfigurer

  • • 分析configure方法可以得出一个结论,根据注册一个UserDetailsService,PasswordEncoder,UserDetailsPasswordService给全局认证管理器构建器注册一个AuthenticationProvider(认证提供者)
class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

   /**
    * 尝试获得有关认证的相关对象
    * @param auth 一般情况都是全局认证管理器
    * @throws Exception
    */
   @Override
   public void configure(AuthenticationManagerBuilder auth) throws Exception {
      if (auth.isConfigured()) {
         return;
      }
      //尝试获取UserDetailsService
      UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
      //如果UserDetailsService都没有,都不能加载用户,也就用不着PasswordEncoder,那就直接返回
      if (userDetailsService == null) {
         return;
      }
      //尝试获取PasswordEncoder
      PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
      //尝试获取UserDetailsPasswordService
      UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
      //创建一个默认的认证提供者,并设置相关属性
      DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
      provider.setUserDetailsService(userDetailsService);
      if (passwordEncoder != null) {
         provider.setPasswordEncoder(passwordEncoder);
      }
      if (passwordManager != null) {
         provider.setUserDetailsPasswordService(passwordManager);
      }
      provider.afterPropertiesSet();
      //给全局认证管理器添加一个默认的认证提供者
      auth.authenticationProvider(provider);
   }

   /**
    * 如果只找到一个bean,则返回被请求类的bean,否则返回null
    * @param type
    */
   private <T> T getBeanOrNull(Class<T> type) {
      String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanNamesForType(type);
      if (beanNames.length != 1) {
         return null;
      }
      return InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
   }

}

1.2.2 InitializeUserDetailsManagerConfigurer

  • • 分析configure方法可以得出,是直接从容器中获取AuthenticationProvider然后注册到全局认证管理器构建器中
class InitializeUserDetailsManagerConfigurer
      extends GlobalAuthenticationConfigurerAdapter {
   @Override
   public void configure(AuthenticationManagerBuilder auth) {
      if (auth.isConfigured()) {
         return;
      }
      AuthenticationProvider authenticationProvider = getBeanOrNull(
            AuthenticationProvider.class);
      if (authenticationProvider == null) {
         return;
      }


      auth.authenticationProvider(authenticationProvider);
   }

   /**
    * @return
    */
   private <T> T getBeanOrNull(Class<T> type) {
      String[] userDetailsBeanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
            .getBeanNamesForType(type);
      if (userDetailsBeanNames.length != 1) {
         return null;
      }

      return InitializeAuthenticationProviderBeanManagerConfigurer.this.context
            .getBean(userDetailsBeanNames[0], type);
   }
}

2、AuthenticationManagerBuilder工作流程

  • • 根据SpringSecurcity的构建流程分为以下几步,前面几步都是在根据上面说的配置类进行初始化
@Override
protected final O doBuild() throws Exception {
   synchronized (this.configurers) {
      this.buildState = BuildState.INITIALIZING;
      beforeInit();
      init();
      this.buildState = BuildState.CONFIGURING;
      beforeConfigure();
      configure();
      this.buildState = BuildState.BUILDING;
      O result = performBuild();
      this.buildState = BuildState.BUILT;
      return result;
   }
}
  • • 唯有performBuild()方法没有看
  • • 作用就是生成ProviderManager(认证管理器)
public class AuthenticationManagerBuilder
      extends
      AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
      implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
    @Override
protected ProviderManager performBuild() throws Exception {
   if (!isConfigured()) {
      this.logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
      return null;
   }
   ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
         this.parentAuthenticationManager);
   //是否在认证成功后是否擦除密码
   if (this.eraseCredentials != null) {
      providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
   }
   //设置认证事件推送器
   if (this.eventPublisher != null) {
      providerManager.setAuthenticationEventPublisher(this.eventPublisher);
   }
   providerManager = postProcess(providerManager);
   return providerManager;
}
}
  • • 流程图

[SpringSecurity5.6.2源码分析五]:AuthenticationConfiguration_源码

image.png