SpringSecurity原理解析(六):SecurityConfigurer 解析
1、SecurityConfigurer
SecurityConfigurer 在 Spring Security 中是一个非常重要的接口,观察HttpSecurity 中的很多
方法可以发现,SpringSecurity 中的每一个过滤器都是通过 xxxConfigurer 来进行配置的,而
这些 xxxConfigurer 其实都是 SecurityConfigurer 的实现。
SecurityConfigurer 是一个接口,其定义如下:
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/**
* Initialize the {@link SecurityBuilder}. Here only shared state should be created
* and modified, but not properties on the {@link SecurityBuilder} used for building
* the object. This ensures that the {@link #configure(SecurityBuilder)} method uses
* the correct shared objects when building. Configurers should be applied here.
* @param builder
* @throws Exception
*
* 初始化 SecurityBuilder
*/
void init(B builder) throws Exception;
/**
* Configure the {@link SecurityBuilder} by setting the necessary properties on the
* {@link SecurityBuilder}.
* @param builder
* @throws Exception
*
* 通过设置必要的属性来配置 SecurityBuilder
*/
void configure(B builder) throws Exception;
}
由 SecurityConfigurer 定义可以发现 init方法和 configure方法的参数都是 SecurityBuilder 的类
型,而SecurityBuilder是用来构建过滤器链 DefaultSecurityFilterChainProxy的,从前边
的笔记可以知道 DefaultSecurityFilterChainProxy 是 SecurityConfigurer 的是实现类
AbstractConfiguredSecurityBuilder中构建的。
SecurityConfigurer 的实现有很多,但我们只需要关注其中三个核心实现,即:
SecurityConfigurerAdapter
GlobalAuthenticationConfigurerAdapter
WebSecurityConfigurer
2、SecurityConfigurerAdapter
SecurityConfigurerAdapter 是 SecurityConfigurer 的基础实现类(抽象类),并没有实现
接口 SecurityConfigurer 的方法,而是在SecurityConfigurer 的基础上扩展了几个方法;
在SpringSecurity 中很多xxxConfigurer也是 SecurityConfigurerAdapter 的子类。
2.1、SecurityConfigurerAdapter 核心属性
SecurityConfigurerAdapter 核心属性只有2个,一个是 SecurityBuilder 类型 另一个是
CompositeObjectPostProcessor,如下图所示:
其中 CompositeObjectPostProcessor 是 SecurityConfigurerAdapter 的一个内部类,并实现
了接口 ObjectPostProcessor,ObjectPostProcessor 是一个后置处理器,
ObjectPostProcessor只有2个实现,即 AutowireBeanFactoryObjectPostProcessor 和
CompositeObjectPostProcessor;
1)AutowireBeanFactoryObjectPostProcessor :
该类主要功能是通过 AutowireCapableBeanFactory 将指定的bean对象注册到spring容器
中,通过前边HttpSecurity 中的方法可以窥见一斑,SpringSecurity 中的对象很多都是直
接new 出来的,这些new手动创建的对象需要手动注册到spring容器中后,在其他地方才
能访问;AutowireBeanFactoryObjectPostProcessor 就是用来将这些new 出来的对象注
册到spring容器的。如下图所示:
2)CompositeObjectPostProcessor
CompositeObjectPostProcessor 则是一个复合的对象处理器,里边维护了一个 List 集
合,这个List 集合中,大部分情况下只存储一条数据,那就是
AutowireBeanFactoryObjectPostProcessor(因为 ObjectPostProcessor 就2个实现,除
了CompositeObjectPostProcessor 自身,剩下的只有
AutowireBeanFactoryObjectPostProcessor ),用来完成对象注入到容器的操作;
2.2、SecurityConfigurerAdapter 常用方法
2.2.1、addObjectPostProcessor
若用户手动调用了该方法,那么CompositeObjectPostProcessor 集合中维护的数据就会增
加一条,CompositeObjectPostProcessor.postProcess 方法中,会遍历集合中的所有
ObjectPostProcessor,挨个调用其 postProcess 方法对对象进行后置处理。
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
}
2.2.2、add()
该方法返回值是一个 securityBuilder,这里securityBuilder 实际上就是 HttpSecurity,我们
在HttpSecurity 中去配置不同的过滤器时,可以使用 and 方法进行链式配置,就是因为这
里定义了 and 方法并返回了 securityBuilder 实例
public B and() {
return getBuilder();
}
protected final B getBuilder() {
Assert.state(this.securityBuilder != null, "securityBuilder cannot be null");
return this.securityBuilder;
}
2.3、SecurityConfigurerAdapter 核心子类
SecurityConfigurerAdapter 的主要子类也是有个,即:
1)UserDetailsAwareConfigurer
2) AbstractHttpConfigurer
3)LdapAuthenticationProviderConfigurer
由于 LDAP 现在使用很少,所以这里重点介绍下前两个
2.3.1、UserDetailsAwareConfigurer
UserDetailsAwareConfigurer是用户配置相关的类,定义如下:
public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
/**
* 返回的是UserDetailsService 接口的实现
* Gets the {@link UserDetailsService} or null if it is not available
* @return the {@link UserDetailsService} or null if it is not available
*/
public abstract U getUserDetailsService();
}
通过定义我们可以看到泛型U必须是UserDetailsService接口的实现,也就是
getUserDetailsService()方法返回的肯定是UserDetailsService接口的实现,还有通
过泛型B及继承SecurityConfigurerAdapter来看,UserDetailsAwareConfigurer类中应该
会构建一个AuthenticationManager对象。
2.3.1.1、AbstractDaoAuthenticationConfigurer
UserDetailsAwareConfigurer 只有一个子类,即 AbstractDaoAuthenticationConfigurer;
AbstractDaoAuthenticationConfigurer 类的主要用于配置DaoAuthenticationProvider;
AbstractDaoAuthenticationConfigurer 类定义如下:
// B--ProviderManagerBuilder
// C--AbstractDaoAuthenticationConfigurer
// U--UserDetailsService
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
extends UserDetailsAwareConfigurer<B, U> {
//声明一个 provider
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//声明一个 UserDetailsService
private final U userDetailsService;
/**
* 传递的对象可以是UserDetailsService或者UserDetailsPasswordService
*/
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
this.userDetailsService = userDetailsService;
this.provider.setUserDetailsService(userDetailsService);
if (userDetailsService instanceof UserDetailsPasswordService) {
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
}
}
/**
* 添加了一个ObjectPostProcessor 后置处理器
*/
@SuppressWarnings("unchecked")
public C withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (C) this;
}
/**
* 添加密码编码器 用于加密
*/
@SuppressWarnings("unchecked")
public C passwordEncoder(PasswordEncoder passwordEncoder) {
this.provider.setPasswordEncoder(passwordEncoder);
return (C) this;
}
public C userDetailsPasswordManager(UserDetailsPasswordService passwordManager) {
this.provider.setUserDetailsPasswordService(passwordManager);
return (C) this;
}
@Override
public void configure(B builder) throws Exception {
//调用后置处理器 将provider添加到SpringIoC容器中
this.provider = postProcess(this.provider);
// 将provider添加到builder对象中
builder.authenticationProvider(this.provider);
}
/**
*
*/
@Override
public U getUserDetailsService() {
return this.userDetailsService;
}
}
AbstractDaoAuthenticationConfigurer 也只有一个子类,即UserDetailsServiceConfigurer
UserDetailsServiceConfigurer
该类的功能是用于在AuthenticationManagerBuilder中配置UserDetailsService。
该类实现的核心是在 configure 方法执行之前加入了 initUserDetailsService 方
法,以方便开发展按照自己的方式去初始化 UserDetailsService;
在 UserDetailsServiceConfigurer 中 initUserDetailsService 是抽象方法,由子类
去实现,如下图所示:
UserDetailsManagerConfigurer类:
UserDetailsManagerConfigurer是 UserDetailsServiceConfigurer 的子类,
UserDetailsManagerConfigurer 中实现了 UserDetailsServiceConfigurer 中定义的
initUserDetailsService 方法,具体的实现逻辑就是将 UserDetailsBuilder 所构建出
来的 UserDetails 以及提前准备好的 UserDetails 中的用户存储到 UserDetailsService
中。
该类同时添加了 withUser 方法用来添加用户,同时还增加了一个 UserDetailsBuilder
用来构建用 户。
UserDetailsManagerConfigurer 有2个子类,分别是:
JdbcUserDetailsManagerConfigurer
InMemoryUserDetailsManagerConfigurer
JdbcUserDetailsManagerConfigurer类:
JdbcUserDetailsManagerConfigurer 在父类的基础上补充了 DataSource 对象,
同时还提供了相应 的数据库查询方法
InMemoryUserDetailsManagerConfigurer类:
InMemoryUserDetailsManagerConfigurer 在父类的基础上重写了构造方法,
将父类中的 UserDetailsService 实例定义为 InMemoryUserDetailsManager,
如下所示:
public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>
extends UserDetailsManagerConfigurer<B, InMemoryUserDetailsManagerConfigurer<B>> {
/**
* Creates a new instance
*/
public InMemoryUserDetailsManagerConfigurer() {
super(new InMemoryUserDetailsManager(new ArrayList<>()));
}
}
2.3.2、AbstractHttpConfigurer
AbstractHttpConfigurer是 在HttpSecurity上运行的SecurityConfigurer实例 的基类;也
就是说 HttpSecurity 上运行的 SecurityConfigurer 都是 AbstractHttpConfigurer 的子类。
AbstractHttpConfigurer 子类有很多,SpringSecurity 中的过滤器配置类都继承
了 AbstractHttpConfigurer,如: FormLoginConfigurer、DefaultLoginPageConfigurer
和 CsrfConfigurer 等等。
AbstractHttpConfigurer 继承于SecurityConfigurerAdapter,并扩展了2个方法,
AbstractHttpConfigurer 定义如下:
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
/**
* getBuilder 中移除相关的 xxxConfigurer,
* getBuilder 方法获取到的实际上就是 HttpSecurity,所以移除掉 xxxConfigurer 实际上就是从过滤器链中移除掉某一个过滤器
*/
@SuppressWarnings("unchecked")
public B disable() {
getBuilder().removeConfigurer(getClass());
return getBuilder();
}
/**
*为配置类添加手动添加后置处理器,返回值是当前配置类,
*
* 该方法常用于链式配置上
*
*/
@SuppressWarnings("unchecked")
public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
}
3、GlobalAuthenticationConfigurerAdapter
GlobalAuthenticationConfigurerAdapter 是一个全局的配置类,使用该类对应的Bean 对象可以
用来配置全局的 AuthenticationManagerBuilder;
GlobalAuthenticationConfigurerAdapter 实现了 SecurityConfigurerAdapter 接口,但是并未对
方法做具体的实现,只是将泛型具体化了
GlobalAuthenticationConfigurerAdapter 定义如下:
@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter
implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
}
}
4、WebSecurityConfigurer
WebSecurityConfigurer 只有一个实现,即 WebSecurityConfigurerAdapter;当我们自定义
SpringSecurity 配置时,需要继承类 WebSecurityConfigurerAdapter;
所以这里 WebSecurityConfigurer 功能就很明确了,即用户扩展用户自定义的配置。
WebSecurityConfigurer 使用示例: