一、前言
在本系列文章:
Spring Security 6.x 系列(4)—— 基于过滤器链的源码分析(一)中着重分析了Spring Security
在Spring Boot
的自动配置、 DefaultSecurityFilterChain
的构造流程、FilterChainProxy
的构造流程。
Spring Security 6.x 系列(7)—— 源码分析之Builder设计模式中详细分析了Spring Security
的Builder
设计模式和WebSecurity
、HttpSecurity
、AuthenticationManagerBuilder
这三个重要构造者公共的部分。
Spring Security 6.x 系列(8)—— 源码分析之配置器SecurityConfigurer接口及其分支实现中分析SecurityConfigurer
接口及其分支实现。
今天沿着Spring Boot
自动配置中对未被介绍的@EnableGlobalAuthentication
进行分析展开。
二、AuthenticationConfiguration
在@EnableWebSecurity
注解中引入了@EnableGlobalAuthentication
注解:
在@EnableGlobalAuthentication
注解上使用@Import(AuthenticationConfiguration.class)
注解引入本类:
我们先看看里面有些什么?
2.1 成员变量
// 状态标志位,AuthenticationManager是否正处于构建过程中
private AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();
// Application容器
private ApplicationContext applicationContext;
// 用于记录所要构建的AuthenticationManager
private AuthenticationManager authenticationManager;
// AuthenticationManager是否已经被构建的标志
private boolean authenticationManagerInitialized;
// 全局认证配置适配器列表
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();
// 对象后处理器
private ObjectPostProcessor<Object> objectPostProcessor;
2.2 核心内容
2.2.1 authenticationManagerBuilder 方法
实例化一个AuthenticationManagerBuilder
类型的构造者,用于构造AuthenticationManager
实例:
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,ApplicationContext context) {/*** Lazy密码加密器:该对象创建时容器中可能还不存在真正的密码加密器* 但是用该lazy密码加密器进行加密或者密码匹配时,会从容器中获取类型为PasswordEncoder的密码加密器,* 如果容器中不存在类型为PasswordEncoder的密码加密器,则使用* PasswordEncoderFactories.createDelegatingPasswordEncoder()创建一个PasswordEncoder供随后加密或者密码匹配使用* LazyPasswordEncoder是定义在当前配置类中的一个内部类*/LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);/***获取认证事件的发布器*/AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);/*** 生成AuthenticationManagerBuilder实例,使用实现类为DefaultPasswordEncoderAuthenticationManagerBuilder* DefaultPasswordEncoderAuthenticationManagerBuilder是定义在该配置类中的一个内部类,它继承自AuthenticationManagerBuilder* 是SpringSecurity缺省使用的 AuthenticationManagerBuilder实现类*/DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);/***如果有事件发布器,则设置*/if (authenticationEventPublisher != null) {result.authenticationEventPublisher(authenticationEventPublisher);}return result;
}private AuthenticationEventPublisher getAuthenticationEventPublisher(ApplicationContext context) {if (context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {return context.getBean(AuthenticationEventPublisher.class);}return this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
}
关于
AuthenticationManagerBuilder
在下文会详细介绍。
2.2.2 getAuthenticationManager 方法
根据配置生成认证管理器 AuthenticationManager
,该方法具有幂等性且进行了同步处理 。
首次调用会触发真正的构建过程生成认证管理器 AuthenticationManager
,再次的调用都会返回首次构建的认证管理器 AuthenticationManager
。
public AuthenticationManager getAuthenticationManager() throws Exception {// authenticationManager如果已经被构建则直接返回authenticationManagerif (this.authenticationManagerInitialized) {return this.authenticationManager;}// 获取容器中的AuthenticationManagerBuilder实例用于创建AuthenticationManagerAuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);// 如果已经正在使用authBuilder进行构建, 则这里直接返回一个包装了构建器authBuilder的AuthenticationManagerDelegator对象// true表示现在正在构建过程中,false表示现在不在构建过程中if (this.buildingAuthenticationManager.getAndSet(true)) {return new AuthenticationManagerDelegator(authBuilder);}// 将全局配置设置到AuthenticationManagerBuilder中for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {authBuilder.apply(config);}// 构建AuthenticationManagerthis.authenticationManager = authBuilder.build();/*** 如果容器中没有用于构建 AuthenticationManager的 AuthenticationProvider bean供authBuilder使用,也没有为 authBuilder 置 parent AuthenticationManager时,* 则上面声明的 authenticationManager为 null。不过这种情况缺省情况下并不会发生:* 因为该配置类中 bean InitializeUserDetailsBeanManagerConfigurer为 authBuilder添加的 InitializeUserDetailsBeanManagerConfigurer * 会在这种情况下构造一个 DaoAuthenticationProvider对象给 authBuilder使用。* 另外,一般情况下,开发人员也会提供自己的 AuthenticationProvider 实现类。* 通常经过上面的 authBuilder.build(),authenticationManager 对象都会被创建,* 但是如果 authenticationManager 未被创建,这里尝试使用 getAuthenticationManagerBean()再次设置 authenticationManage*/if (this.authenticationManager == null) {this.authenticationManager = getAuthenticationManagerBean();}//将authenticationManagerInitialized 设置为true,说明authenticationManager已经初始化完成this.authenticationManagerInitialized = true;// 返回构建好的AuthenticationManagerreturn this.authenticationManager;
}
2.2.3 初始化GlobalAuthenticationConfigurerAdapter的三个实现
定义一个EnableGlobalAuthenticationAutowiredConfigurer
,他会加载使用了注解@EnableGlobalAuthentication
的Bean
,用于配置全局AuthenticationManagerBuilder
:
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {private final ApplicationContext context;private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);EnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {this.context = context;}@Overridepublic void init(AuthenticationManagerBuilder auth) {Map<String, Object> beansWithAnnotation = this.context.getBeansWithAnnotation(EnableGlobalAuthentication.class);if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Eagerly initializing %s", beansWithAnnotation));}}}
定义一个InitializeUserDetailsBeanManagerConfigurer
配置类,用于配置单例的UserDetailsService
时延时配置全局AuthenticationManagerBuilder
:
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {// InitializeUserDetailsBeanManagerConfigurer是GlobalAuthenticationConfigurerAdapter的一个实现return new InitializeUserDetailsBeanManagerConfigurer(context);
}
定义一个InitializeAuthenticationProviderBeanManagerConfigurer
配置类,用于配置单例的UserDetailsService
时延时加载全局AuthenticationProvider
:
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {// InitializeAuthenticationProviderBeanManagerConfigurer是GlobalAuthenticationConfigurerAdapter的一个实现return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
2.2.4 AuthenticationManagerDelegator
AuthenticationManagerDelegator
是AuthenticationManager
的一个包装类或是委托类,主要是为了防止在初始化AuthenticationManager
时发生无限递归:
- 当这个内部类被构建时,会注入一个
AuthenticationManagerBuilder
实例。 authenticate()
方法具有幂等性且进行了同步处理- 当这个类的
authenticate()
方法被第一次调用时会使用AuthenticationManagerBuilder
创建一个AuthenticationManager
保存到这个类的delegate
属性中,同时将delegateBuilder
置空,然后将实际鉴权处理交给AuthenticationManager
。 - 后续再调用
authenticate()
方法就只是使用已经创建好的AuthenticationManager
实例。
- 当这个类的
static final class AuthenticationManagerDelegator implements AuthenticationManager {private AuthenticationManagerBuilder delegateBuilder;private AuthenticationManager delegate;private final Object delegateMonitor = new Object();// 初始化一个AuthenticationManagerBuilder实例AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");this.delegateBuilder = delegateBuilder;}// 具有幂等性且进行了同步处理@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {// 如果已经包含创建成功的AuthenticationManager,直接调用AuthenticationManager.authenticate()方法返回一个Authenticationif (this.delegate != null) {return this.delegate.authenticate(authentication);}// 如果没有包含创建成功的AuthenticationManager,进入同步方法synchronized (this.delegateMonitor) {if (this.delegate == null) {// 使用AuthenticationManagerBuilder构建一个AuthenticationManager,// 将值设置到AuthenticationManagerDelegator的delegate属性this.delegate = this.delegateBuilder.getObject();this.delegateBuilder = null;}}// 调用AuthenticationManager.authenticate()方法返回一个Authenticationreturn this.delegate.authenticate(authentication);}@Overridepublic String toString() {return "AuthenticationManagerDelegator [delegate=" + this.delegate + "]";}
}
三、AuthenticationManagerBuilder
3.1 继承关系
在前面文章了解过 WebSecurity
、HttpSecurity
、AuthenticationManagerBuilder
这三个重要构造者有一条继承树:
|- SecurityBuilder|- AbstractSecurityBuilder|- AbstractConfiguredSecurityBuilder
3.2 ProviderManagerBuilder
ProviderManagerBuilder
是一个接口,继承 SecurityBuilder
,也是一个构造器,它构造的对象是 AuthenticationManager
(认证管理器 ):
源码注释:
用于执行创建ProviderManager
的SecurityBuilder
(构造器) 接口
/*** Interface for operating on a SecurityBuilder that creates a {@link ProviderManager}** @param <B> the type of the {@link SecurityBuilder}* @author Rob Winch*/
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>>extends SecurityBuilder<AuthenticationManager> {/*** Add authentication based upon the custom {@link AuthenticationProvider} that is* passed in. Since the {@link AuthenticationProvider} implementation is unknown, all* customizations must be done externally and the {@link ProviderManagerBuilder} is* returned immediately.** Note that an Exception is thrown if an error occurs when adding the* {@link AuthenticationProvider}.* @return a {@link ProviderManagerBuilder} to allow further authentication to be* provided to the {@link ProviderManagerBuilder}*/B authenticationProvider(AuthenticationProvider authenticationProvider);}
它只有两个抽象方法:
-
build()
方法继承自父类,用来构建AuthenticationManager
对象 -
authenticationProvider(AuthenticationProvider authenticationProvider)
方法这个方法由子类实现,源码对这个方法的描述:
- 根据传入的自定义
AuthenticationProvider
添加身份验证(authentication
)。 - 由于
AuthenticationProvider
实现未知,因此所有自定义都必须在外部完成并立即返回
ProviderManagerBuilder
。 - 添加过程中出错会抛异常。
- 根据传入的自定义
个人理解:
这个接口对父接口扩展的主要点在于以authenticationProvider(AuthenticationProvider authenticationProvider)
方法的形式向构造器添加自定义的认证提供者(AuthenticationProvider
),每次添加完之后会立即返回添加完认证提供者之后的构造器,这样就可以利用这个返回对象继续添加向其认证提供者(AuthenticationProvider
)。