一、前言
在本系列文章:
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如果已经被构建则直接返回authenticationManager
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
// 获取容器中的AuthenticationManagerBuilder实例用于创建AuthenticationManager
AuthenticationManagerBuilder 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);
}
// 构建AuthenticationManager
this.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;
// 返回构建好的AuthenticationManager
return 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;
}
@Override
public 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;
}
// 具有幂等性且进行了同步处理
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 如果已经包含创建成功的AuthenticationManager,直接调用AuthenticationManager.authenticate()方法返回一个Authentication
if (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()方法返回一个Authentication
return this.delegate.authenticate(authentication);
}
@Override
public 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
)。