持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
SecurityBuilder的功能
Spring Security 中所有需要构建的对象都可以通过SecurityBuilder来实现,比如说这些默认的过滤器链、代理过滤器、AuthenticationManager等组件,都由SecurityBuilder 进行构建。SecurityBuilder源码本身比较简单,只有一个build方法,返回具体构建的对象泛型O,不同的实现可以构造出不同的SecurityBuilder对象,但是,SecurityBuilder的继承和实现比较复杂,关系图如下。
1. HttpSecurityBuilder
HttpSecurityBuilder是用来构建HttpSecurity对象的,HttpSecurityBuilder 也是一个接口,其代码如下:
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
extends SecurityBuilder<DefaultSecurityFilterChain> {
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);
<C> void setSharedObject(Class<C> sharedType, C object);
<C> C getSharedObject(Class<C> sharedType);
H authenticationProvider(AuthenticationProvider authenticationProvider);
H userDetailsService(UserDetailsService userDetailsService) throws Exception;
H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);
H addFilter(Filter filter);
}
我们简单阅读一下这段源码,看一下代码上的注释,了解了解功能:
- HtpSecunityBuilder对象本身在定义时就有一个泛型,这个泛型采用了上界通配符,但是HtpSecunityBuilder的子类默认情况下就只有一个,那就是HtpSecurity,所以在阅读的时暂且把接口HtpSecunityBuilder中的H都当成HttpSecurity来理解。
- HttpSecurityBuilder 继承自 SecurityBuilder 接口,同时也指定了SecurityBuilder 中的泛型为DefaultSecurityFilterChain,也就是说,HttpSecurityBuilder 想要构建的对象是 DefaultSecurityFilterChain。
- getConfigurer 方法是通过类名来获取一个配置器,所谓的配置器就是一个具体的Configurer配置类。
- removeConfigurer方法也是按类名移除一个配置器(相当于从Spring Security过滤器链中移除一个过滤器)。
- setSharedObject/getSharedObject 这两个方法用来设置或者获取一个由多个配置器共享的对象。
- authenticationProvider 方法可以用来配置一个认证器AuthenticationProvider.
- userDetailsService 方法可以用来配置一个数据源UserDetailsService。
- addFilterAfter/addFilterBefore 方法表示在某一个过滤器之后或者之前添加一个自定义的过滤器。
- addFilter 方法可以添加一个过滤器,这个过滤器必须是Spring Security 框架提供的过滤器的一个实例或者其扩展,添加完成后,会自动进行过滤器的排序。
2. AbstractSecurityBuilder
AbstractSecurityBuilder是一个抽象类,它实现了SecurityBuilder 接口,并实现了build方法,它是一个基本的SecurityBuilder,主要功能就是确保正在构建的对象只构建一次,源码如下:
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
@Override
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
protected abstract O doBuild() throws Exception;
}
首先,声明了building 变量,这是一个可以自动更新的boolean值,确保即使在多线程环境下,配置类也只能构建一次。
然后,对build方法进行重写,并且设置为final, 这样在AbstractSecurityBuilder的子类中
将不能覆盖build方法了。在build方法内部,通过building变量来控制配置类只构建一次,否则就会抛异常,具体的构建操作交由doBuild方法去完成。
其次,getObject 方法用来返回构建的对象。
最后,doBuild方法是具体的构建方法,该访法在AbstractSecurityBuilder中是一个抽象方法,那么其具体的实现则在其子类中。
3. AbstractConfiguredSecurityBuilder
AbstractConfiguredSecurityBuilder类里面有一个枚举类,这个枚举类主要列举了构建过程中的状态。
AbstractConfiguredSecurityBuilder类里维护了configurers变量,这是一个Map类型的,用来保存所有的配置类。针对configurers 变量,我们可以进行添加配置、移除配置等操作,其主要作用如下:
- 首先声明了一个configurers变量,用来保存所有的配置类, key是配置类Class对象,value是一个List集合中放着的配置类列表。
- apply方法有两个,参数类型略有差异,主要功能基本相同,都是向configurers 变量中添加配置类,具体的添加过程则是调用该类里的add方法。
- add方法用来将所有的配置类保存到configurers 中,在添加的过程中,如果 allowConfigurersOfSameType变量为true,也就是表示允许相同类型的配置类存在,表示List集合中可以存在多个相同类型的配置类。默认情况下,List集合中的配置类只有一个配置类; 如果在AuthenticationManagerBuilder中设置allowConfigurersOfSameType为true,此时相同类型的配置类可以有多个。
- getConfigurers(Class)方法可以从configurers中返回某个配置类对应的所有实例。而getConfigurer方法也是获取配置类实例,但是只获取集合中第一项。
- removeConfigurers方法可以从configurers中移除某个配置类对应的所有实例,并返回被移除掉的配置类的实例集合。removeConfigurer方法可以从configurers中移除某一个配置类对应的所有配置类实例,并返回被移除掉的配置类实例中的第一项。
- getConfigurers方法是一个私有方法,主要是把所有的配置类实例放到一个集合中返回。在配置类初始化和配置的时候,会调用到该方法。
接下来才是AbstractConfiguredSecurityBuilder的重头戏——doBuild方法,doBuild方法也该类的核心构建方法。具体代码如下:
@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;
}
}
protected void beforeInit() throws Exception {
}
protected void beforeConfigure() throws Exception {
}
protected abstract O performBuild() throws Exception;
@SuppressWarnings("unchecked")
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
configurer.init((B) this);
}
}
@SuppressWarnings("unchecked")
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;
}
- 在doBuild方法中,步骤比较清晰,更新构建状态的同时执行构建方法。构建方法中, beforeInit是一个空的初始化方法,如果需要在初始化之前做准备工作,则可以通过重写该方法进行准备工作。
- init方法是初始化方法,也可以理解为所有配置类的初始化方法,在该方法中,主要操作就是对配置类进行遍历,然后调用其init方法完成初始化操作。
- beforeConfigure方法可以在configure方法执行之前做一些准备操作。该方法默认也是一个空方法。
- configure方法用来完成所有配置类的配置,跟init方法类似,在configure方法中,也是遍历所有的配置类,再分别调用各自的configure方法进行配置。
- performBuild方法用来做最后的构建操作,前面的准备工作完成后,最后在performBuild方法中完成构建,这是一个抽象方法,具体的实现过程则在不同的配置类中。
这些就是AbstractConfiguredSecurityBuilder 中最主要的几个方法了