[SpringSecurity5.2.2源码分析五]:AuthenticationConfiguration

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

1、重要方法

1.1 authenticationManagerBuilder()

  • 作用是往容器中注册一个全局认证管理器构建器
java 复制代码
@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达到一个懒加载的作用
java 复制代码
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
java 复制代码
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(认证提供者)
java 复制代码
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然后注册到全局认证管理器构建器中
java 复制代码
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的构建流程分为以下几步,前面几步都是在根据上面说的配置类进行初始化
java 复制代码
@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(认证管理器)
java 复制代码
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;
}
}
  • 流程图
相关推荐
冬奇Lab11 小时前
Agent 系列(9):多 Agent 架构设计模式——Supervisor 与 Pipeline
人工智能·源码·agent
爱笑的源码基地2 天前
智慧班牌源码:从后端SpringBoot到前端Vue2的全栈实现
java·大数据·云计算·源码·程序代码·智慧校园源码·智慧班牌源码
源码宝2 天前
MES系统源码:Java8 + SpringBoot2.7 + MySQL8 + Redis,后端源码清爽易扩展
java·后端·源码·springboot·mes系统·源码二开·mes源码
魏思凡3 天前
Glide 源码学习系列
源码·glide
谷哥的小弟4 天前
图文详解Spring Boot整合MyBatisPlus(附源码)
mybatis·源码·springboot·mybatis-plus·整合
元思未来5 天前
Hermes Agent 源码探秘 (9):实战案例 — 我是怎么用 Hermes 的
agent·源码阅读
元思未来6 天前
Hermes Agent 源码探秘 (7):记忆与技能 — Agent 的"学习"能力
agent·源码阅读
元思未来6 天前
Hermes Agent 源码探秘 (8):子代理系统 — Agent 生 Agent
agent·源码阅读
万岳科技程序员小金6 天前
真人数字人系统源码开发指南:一套平台如何支撑多端应用(APP/小程序)
源码·软件开发·ai数字人小程序·ai数字人系统源码·ai真人数字人app开发·数字人平台搭建
工业互联网专业7 天前
国潮男装微博评论数据分析系统的设计与实现 _flask+spider
python·flask·毕业设计·源码·课程设计·spider