UsernamePasswordAuthenticationFilter中的authenticationManager到底是谁注入的

继承关系

UsernamePasswordAuthenticationFilter继承至AbstractAuthenticationProcessingFilter,里面有个 AuthenticationManager认证管理的属性,那么这个属性是谁注入进来的,是全局的authenticationManager 还是局部的authenticationManager吗?接下来分析一下。

java 复制代码
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
      implements ApplicationEventPublisherAware, MessageSourceAware {
       // 认证管理器
       private AuthenticationManager authenticationManager;
   }

FormLoginConfigurer

UsernamePasswordAuthenticationFilter是由FormLoginConfigurer配置类构建的

FormLoginConfigurer实际上实现了SecurityConfigurer接口。我们知道httpSecurity在调用build()方法构建过滤器链的时候,会调用父类的doBuild方法

ini 复制代码
@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;
   }
}

里面有两个关键步骤 叫beforeConfigure();和 configure();

beforeConfigure()

httpSecurity重写了beforeConfigure()方法 如下:总结起来就是构建一个局部的AuthenticationManager放入共享对象,留着后续这个httpSecurity的构建过程中使用

scss 复制代码
@Override
protected void beforeConfigure() throws Exception {
   
   if (this.authenticationManager != null) {
      setSharedObject(AuthenticationManager.class, this.authenticationManager);
   }
   else {
      ObservationRegistry registry = getObservationRegistry();
      // 最终会调用getAuthenticationRegistry从SharedObject拿到属于自己的那个局部的AuthenticationManagerBuilder进行构建一个独立的认证管理器
      AuthenticationManager manager = getAuthenticationRegistry().build();
      if (!registry.isNoop() && manager != null) {
         setSharedObject(AuthenticationManager.class, new ObservationAuthenticationManager(registry, manager));
      }
      else {
         //最后将这个认证管理器放入共享对象中 留着后续configure的时候用到
         setSharedObject(AuthenticationManager.class, manager);
      }
   }
}
csharp 复制代码
private AuthenticationManagerBuilder getAuthenticationRegistry() {
   return getSharedObject(AuthenticationManagerBuilder.class);
}

configure()

configure()方法就是从configurers中获取所有的SecurityConfigurer<O, B> configurer,依次调用他们的configure方法,

swift 复制代码
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();

private void configure() throws Exception {
   Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
   for (SecurityConfigurer<O, B> configurer : configurers) {
      configurer.configure((B) this);
   }
}

private Collection<SecurityConfigurer<O, B>> getConfigurers() {
   List<SecurityConfigurer<O, B>> result = new ArrayList<>();
   for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
      result.addAll(configs);
   }
   return result;
}

我们上面看继承关系的时候看到FormLoginConfigurer实际上实现了SecurityConfigurer接口,所以他就是其中一个,会调用FormLoginConfigurer的configure()

从中我们看到这个代码 this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class))

也就是说configure方法就是用来配置UsernamePasswordAuthenticationFilter的属性的,其中的AuthenticationManager的属性就是这步配置的,从上一步的共享对象中获取到认证管理器

kotlin 复制代码
@Override
public void configure(B http) throws Exception {
   PortMapper portMapper = http.getSharedObject(PortMapper.class);
   if (portMapper != null) {
      this.authenticationEntryPoint.setPortMapper(portMapper);
   }
   RequestCache requestCache = http.getSharedObject(RequestCache.class);
   if (requestCache != null) {
      this.defaultSuccessHandler.setRequestCache(requestCache);
   }
   this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
   this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
   this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
   if (this.authenticationDetailsSource != null) {
      this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
   }
   SessionAuthenticationStrategy sessionAuthenticationStrategy = http
      .getSharedObject(SessionAuthenticationStrategy.class);
   if (sessionAuthenticationStrategy != null) {
      this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
   }
   RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
   if (rememberMeServices != null) {
      this.authFilter.setRememberMeServices(rememberMeServices);
   }
   SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
   if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
      SecurityContextRepository securityContextRepository = securityContextConfigurer
         .getSecurityContextRepository();
      this.authFilter.setSecurityContextRepository(securityContextRepository);
   }
   this.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
   F filter = postProcess(this.authFilter);
   http.addFilter(filter);
}

总结 也就是说我们实际在表单登录的时候里面认证用到的实际是局部创建的这个认证管理器,但是我们知道认证管理器的实现类是providerManager,providerManager在认证的时候需要依赖providers维护的实际提供者来进行认证,但是我们局部的认证管理器在创建的时候没有提供合适的providers,这个集合为空,实际也不是为空,里面有个AnonymousAuthenticationProvider,但是它不支持UsernamePasswordToken的认证, AnonymousAuthenticationProvider什么时候放进去的我们后面再说。所以最后调用parent全局的认证管理器来进行验证

java 复制代码
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {

   private static final Log logger = LogFactory.getLog(ProviderManager.class);

   private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();

   private List<AuthenticationProvider> providers = Collections.emptyList();

   protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

   private AuthenticationManager parent;
相关推荐
快乐非自愿8 分钟前
Java垃圾收集器全解:从Serial到G1的进化之旅
java·开发语言·python
树在风中摇曳13 分钟前
Java 静态成员与继承封装实战:从报错到彻底吃透核心特性
java·开发语言
hweiyu003 小时前
Go Fiber 简介
开发语言·后端·golang
键来大师4 小时前
Android15 RK3588 修改默认不锁屏不休眠
android·java·framework·rk3588
合作小小程序员小小店5 小时前
web网页开发,在线%考试管理%系统,基于Idea,vscode,html,css,vue,java,maven,springboot,mysql
java·前端·系统架构·vue·intellij-idea·springboot
你的人类朋友5 小时前
😎 Node.js 应用多阶段构建 Dockerfile 详解
后端·docker·容器
小坏讲微服务6 小时前
Spring Boot整合Redis注解,实战Redis注解使用
spring boot·redis·分布式·后端·spring cloud·微服务·mybatis
多多*6 小时前
maven常用的命令
java·log4j·maven
xie_pin_an6 小时前
MyBatis-Plus 实战:MPJLambdaWrapper 多表联查用法全解析
java·spring boot·spring·mybatis
ᐇ9596 小时前
Java LinkedList集合全面解析:双向链表的艺术与实战
java·开发语言·链表