官网地址 Spring Security Reference
版本:Version 5.5.0
AuthenticationManagerBuilder 的继承关系图
在前面了解过 WebSecurity、HttpSecurity、AuthenticationManagerBuilder 这三个重要构建者公共的部分:
|- SecurityBuilder
|- AbstractSecurityBuilder
|- AbstractConfiguredSecurityBuilder
公共的这部分对构建者做扩展,点击这里可以回顾一下,这里主要看看 AuthenticationManagerBuilder 类。
ProviderManagerBuilder 接口
继承 SecurityBuilder
,也是一个构建器,它构建的对象是 AuthenticationManager
(认证管理器 )。
源码注释对这个接口的说明:用于操作创建 ProviderManager 的 SecurityBuilder 的接口
它只有两个抽象方法:
- build() 方法继承自父类,用来构建对象(
AuthenticationManager
) - authenticationProvider(AuthenticationProvider authenticationProvider) 方法
这个方法由子类实现,源码对这个方法的描述:- 根据传入的自定义 AuthenticationProvider 添加(authentication )身份验证。
- 由于 AuthenticationProvider 实现未知,因此所有自定义都必须在外部完成并立即返回 ProviderManagerBuilder。
- 添加过程中出错会抛异常
个人理解,这个接口对父接口扩展的主要点在于:以AuthenticationProvider的形式向构建器添加自定义的身份验证供应者,每次添加完之后会立即返回添加完身份验证供应者之后的构建器,这样就可以利用这个返回对象继续添加身份验证供应者。
这里提到的“身份验证供应者” 是根据类名起的,表示 AuthenticationProvider 。AuthenticationProvider 的具体实现在外部完成,这样构建器久不需要关注认证的具体实现,只需要关注构建“认证管理器”(
AuthenticationManager
)。
这里提到了一个新的类或接口,先简单了解一下它们的作用:
- AuthenticationProvider 接口:
表示一个类可以处理特定的身份验证实现。每一个实现类都提供一个特定的身份认证实现。 - AuthenticationManager 接口:处理身份认证请求的认证管理器
- ProviderManager 类:AuthenticationManager 的一个实现类,实现通过 AuthenticationProviders 列表迭代进行身份验证请求。
AuthenticationManagerBuilder
官方对这个类的概述:SecurityBuilder 用于创建 AuthenticationManager。 允许轻松地构建内存身份验证管理器、LDAP 身份验证管理器、基于 JDBC 的身份验证管理器,允许轻松地添加 UserDetailsService 和添加 AuthenticationProvider。
成员变量
- parentAuthenticationManager 父级认证管理器
- authenticationProviders 存放身份验证供应者的集合(一个List)
- defaultUserDetailsService 默认用户信息服务,帮助获取用户数据
- eraseCredentials 擦除凭证
- eventPublisher 事件发布器
构造方法
调用父类 AbstractConfiguredSecurityBuilder 的构造方法,并且传入的 allowConfigurersOfSameType
值为 true ,表示创建的这个构建器支持应用同类型的配置器。构造方法接收一个后置处理器对象。
详细可参考【Spring Security】—— WebSecurity、HttpSecurity、AuthenticationManagerBuilder 构建者的共同特性
authenticationProvider 方法
这个方法是对 ProviderManagerBuilder#authenticationProvider方法的具体实现实现,用来为构建器添加身份验证供应者,并在添加完成之后立刻返回这个构建器。
所有添加到 AuthenticationManagerBuilder 的身份验证供应者(AuthenticationProvider)都将被存储在构建器(AuthenticationManagerBuilder)的 authenticationProviders 成员变量中。
performBuild 方法
根据之前看的父类 AbstractConfiguredSecurityBuilder 的源码,可以知道,最终的构建逻辑实现交给了子类的 performBuild 方法。
- AbstractSecurityBuilder
对 build 方法做了实现:对象只允许构建一次 ,并把构建工作交给子类的 doBuild 方法- AbstractConfiguredSecurityBuilder
对 doBuild 方法做了实现:增加了构建过程的生命周期,提供了可以切入生命周期的两个方法,并将具体构建交给 performBuild 方法,该方法由子类实现。
@Override
protected ProviderManager performBuild() throws Exception {
/**
* 先判断是否已经完成了配置:
* 确保 authenticationProviders 中有 AuthenticationProvider
* 或者该构建器指定了 parentAuthenticationManager
*/
if (!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
// 创建对象: ProviderManager 是 AuthenticationManager 的一个实现类
ProviderManager providerManager = new ProviderManager(authenticationProviders,
parentAuthenticationManager);
// 设置擦除凭证(在看AuthenticationManager时再细看)
if (eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
}
// 设置事件发布(在看AuthenticationManager时再细看)
if (eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(eventPublisher);
}
// 调用后置处理
providerManager = postProcess(providerManager);
// 返回构建的对象
return providerManager;
}
/**
* 根据源码的描述,这个方法同时可以确保 AuthenticationManagerBuilder 对象以及完成了配置过程
*/
public boolean isConfigured() {
return !authenticationProviders.isEmpty() || parentAuthenticationManager != null;
}
源码注解中还有一个提示:
This method does NOT ensure that the UserDetailsService is available for the getDefaultUserDetailsService() method. Note that an Exception might be thrown if an error occurs when adding the AuthenticationProvider.
- 这个方法不确保 UserDetailsService 可用于 getDefaultUserDetailsService() 方法。(?????标记一下后面再理解 ????)
- 如果在添加 AuthenticationProvider 过程中发生错误,会抛出异常。
一些可以快速构建身份验证管理器的方法
这几个方法都类似,通过创建一个特殊的认证配置器应用到构建器,最后返回创建的构建器,便于针对这些配置器做自定义的设置。
-
inMemoryAuthentication 方法
将内存身份验证添加到 AuthenticationManagerBuilder 并返回 InMemoryUserDetailsManagerConfigurer 以允许自定义内存身份验证。 -
jdbcUserDetailsManagerConfigurer 方法
将 JDBC 身份验证添加到 AuthenticationManagerBuilder 并返回 JdbcUserDetailsManagerConfigurer 以允许自定义 JDBC 身份验证。并且建议使用使用 Flyway 或 Liquibase 之类的工具欸管理数据库,建议SQL优化。 -
ldapAuthenticationProviderConfigurer 方法
将 LDAP 身份验证添加到 AuthenticationManagerBuilder 并返回 LdapAuthenticationProviderConfigurer 以允许自定义 LDAP 身份验证。
源码注解中提到:inMemoryAuthentication 方法和 jdbcUserDetailsManagerConfigurer 方法可以确保 UserDetailsService 可用于 getDefaultUserDetailsService() 方法【意思是说:确保 getDefaultUserDetailsService() 方法可以获取到一个可用的 UserDetailsService 对象 】,但是 ldapAuthenticationProviderConfigurer 方法不保证这一点。
处于多出都提到了这一点,所有决定立刻解决这个问题。
首先,这三个方法都调用了 apply 方法
通过 AuthenticationManagerBuilder 的源码发现一个 apply 方法:
private <C extends UserDetailsAwareConfigurer<AuthenticationManagerBuilder, ? extends UserDetailsService>> C apply(
C configurer) throws Exception {
this.defaultUserDetailsService = configurer.getUserDetailsService();
return (C) super.apply(configurer);
}
这个方法的作用与父类的 apply 方法很相似,唯一多了一点:this.defaultUserDetailsService = configurer.getUserDetailsService();
,也就是说,在将配置器引用到构建器时,会从配置器中获取 UserDetailsService 赋值给成员变量 defaultUserDetailsService ,这样就确保了构建器的 getDefaultUserDetailsService() 方法过去到的 UserDetailsService 可用。
再搜寻上面三个方法中各种创建的配置器(***Configurer),发现除了 LdapAuthenticationProviderConfigurer 类中找不到 getUserDetailsService 方法,其他两个都有。
到这里就算理解源码之间中提到的这句话了:This method also ensure that the UserDetailsService is available for the getDefaultUserDetailsService() method.
userDetailsService 方法
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
T userDetailsService) throws Exception {
this.defaultUserDetailsService = userDetailsService;
return apply(new DaoAuthenticationConfigurer<>(
userDetailsService));
}
根据传入的自定义 UserDetailsService 添加身份验证。然后返回一个 DaoAuthenticationConfigurer 以允许自定义身份验证。并且此方法还确保 UserDetailsService 可用于 getDefaultUserDetailsService() 方法。另外,其他的 UserDetailsService 可能会覆盖此 UserDetailsService 作为默认值。
其他方法
略。
总结
应用官网描述:
AuthenticationManagerBuilder用于创建AuthenticationManager。 允许轻松构建内存身份验证,LDAP身份验证,基于JDBC的身份验证,添加UserDetailsService以及添加AuthenticationProvider。