官网地址 Spring Security Reference
版本:Version 5.5.0
概述
WebSecurity、HttpSecurity、AuthenticationManagerBuilder 都是框架中的构建者,把他们放到一起看看他们的共同特点:
首先还是利用idea工具看看他们的继承实现关系图:
1. AuthenticationManagerBuilder
2. WebSecurity
3. HttpSecurity
可以看出(红色线框标记的部分),他们都有这样一条继承树:
|- SecurityBuilder
|- AbstractSecurityBuilder
|- AbstractConfiguredSecurityBuilder
SecurityBuilder
/**
* Interface for building an Object
*
* @author Rob Winch
* @since 3.2
*
* @param <O> The type of the Object being built
*/
public interface SecurityBuilder<O> {
/**
* Builds the object and returns it or null.
*
* @return the Object to be built or null if the implementation allows it.
* @throws Exception if an error occurred when building the Object
*/
O build() throws Exception;
}
当调用它的 build() 方法时,会创建一个对象。将要创建的对象由泛型 O 限制。这个接口是所有构建者的顶级接口,也是Spring Security 框架中使用的建造者模式的基础接口。
AbstractSecurityBuilder
/**
* A base SecurityBuilder that ensures the object being built is only built one time.
* @param <O> the type of Object that is being built
* @author Rob Winch
*/
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
// 标记对象是否处于创建中
private AtomicBoolean building = new AtomicBoolean();
private O object;
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
// 对象的实际底构建过程再 doBuild() 方法中实现
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
/**
* Gets the object that was built. If it has not been built yet an Exception is thrown.
* @return the Object that was built
*/
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
/**
* Subclasses should implement this to perform the build.
* 子类需要事项这个方法,执行对象的构建
* @return the object that should be returned by {@link #build()}.
*
* @throws Exception if an error occurs
*/
protected abstract O doBuild() throws Exception;
}
这个是 SecurityBuilder 的一个基本实现抽象类,能够确保对象只被创建一次,这个类很简单:
- 定义了一个原子操作的对象,用来标记当前对象是否处于构建中
private AtomicBoolean building = new AtomicBoolean();
- 实现接口的 build() 方法
调用 doBuild() 方法完成构建,并且在调用 doBuild() 之前需要原子修改 building 为 true,只有修改成功才能执行 doBuild() 方法,这间接的保证了:对象只构建一次,构建的唯一性和原子性。 - 增加了一个 getObject() 方法,方便获取构建的对象
- 抽象方法 doBuild() ,交给子类自行实现
AbstractConfiguredSecurityBuilder
官网注释:
A base SecurityBuilder that allows SecurityConfigurer to be applied to it. This makes modifying the SecurityBuilder a strategy that can be customized and broken up into a number of SecurityConfigurer objects that have more specific goals than that of the SecurityBuilder.
一个基本的SecurityBuilder,允许将SecurityConfigurer应用于它。这使得修改SecurityBuilder的策略可以自定义并分解为许多SecurityConfigurer对象,这些对象具有比SecurityBuilder更具体的目标。
For example, a SecurityBuilder may build an DelegatingFilterProxy, but a SecurityConfigurer might populate the SecurityBuilder with the filters necessary for session management, form based login, authorization, etc.
它继承自 AbstractSecurityBuilder ,在此之上又做了一些扩展。先来看看里面都有什么:
内部有一个静态枚举类 BuildState
这个枚举类用来表示应用程序(构建器构建对象)的状态,代码相对简单,就不粘贴源码了。
枚举类中只有一个 int 类型的成员变量 order
表示状态编号:
UNBUILT(0) --------------- 未构建
构建器的 build 方法被调用之前
INITIALIZING(1) ---------- 初始化中
构建器的 build 方法第一次被调用,到所有 SecurityConfigurer 的 init 方法都被调用完这期间都是 INITIALIZING 状态
CONFIGURING(2) -------- 配置中
表示从所有的 SecurityConfigurer 的 init 方法都被调用完,直到所有 configure 方法都被调用
意思就是所有配置器都初始化了,直到配置都被调用这段时间都时 CONFIGURING 状态
BUILDING(3) --------------- 对象构建中
表示已经执行完所有的 SecurityConfigurer 的 configure 方法,到刚刚执行完 AbstractConfiguredSecurityBuilder 的 performBuild 方法这期间
意思就是从将所有配置器的配置都配置完成开始,到构建完这个对象这段时间都是 BUILDING 状态
BUILT(4) ---------------------- 对象已经构建完成
表示对象已经构建完成。
枚举类中还有两个方法:
isInitializing
INITIALIZING状态返回true
isConfigured
大于等于 CONFIGURING 的时候返回 true,也就是说配置器初始化完成时在构建者看来就算以配置状态了
成员变量
- configurers
所要应用到当前 SecurityBuilder 上的所有的 SecurityConfigurer - configurersAddedInInitializing
用于记录在初始化期间添加进来的 SecurityConfigurer - sharedObjects
共享对象 - ObjectPostProcessor
方法
构造方法
/***
* Creates a new instance with the provided ObjectPostProcessor. This post processor must support Object since there are many types of objects that may be post processed.
*/
protected AbstractConfiguredSecurityBuilder( ObjectPostProcessor<Object> objectPostProcessor) {
this(objectPostProcessor, false);
}
/***
* @param objectPostProcessor the ObjectPostProcessor to use
* @param allowConfigurersOfSameType if true, will not override other SecurityConfigurer's when performing apply
*/
protected AbstractConfiguredSecurityBuilder(
ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
}
构造函数只对两个成员变量进行了赋值:
objectPostProcessor
由外部调用者提供,这是一个后置处理对象,在创建完对象后会用到这个后置处理对象。allowConfigurersOfSameType
从源码可以看出,默认情况下allowConfigurersOfSameType
是false
。这个成员变量的含义:
- true 表示允许相同类型的配置器,在应用配置器时不会覆盖相同类型的配置器在源码中搜索了一下这个成员变量,在 add 方法中找到了使用它的代码,具体内容在下面 add 方法中遇到再做说明。
getOrBuild 方法
想要用构建器获取构建的对象时,通过构建器对象调用这个方法。这个方法的逻辑很简单,调用 isUnbuilt()
方法判断对象创建状态是否未创建完成( return buildState == BuildState.UNBUILT
),如果对象已经创建就直接返回已经构建好的对象,否则调用构建器的 build()
方法构建对象并返回构建完成的对象。从刚才看到的父类 AbstractSecurityBuilder 代码中可以知道真正的构建过程是调用子类 doBuild()
方法完成的。
isUnbuilt() 方法中,对 configurers 成员变量加了锁(synchronized),保证获取到的构建完成状态时,对象真的已经构建好了。
private boolean isUnbuilt() {
synchronized (configurers) {
return buildState == BuildState.UNBUILT;
}
}
doBuild 方法
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
- 构建过程对 configurers 加锁
- 方法体中时构建对象的整个流程,包括状态的转移
- 构建过程大致分为构建器初始化 beforeInit(),init(),构建器配置 beforeConfigure(),configure(),构建对象 performBuild()。
构建过程对 configurers 加锁,也就意味着进入构建方法后 configurers 中的构建器应该都准备好了。这个时候如果再添加或者修改配置器都会失败。
beforeInit 方法 和 beforeConfigure 方法
这两个方法是抽象方法,由子类实现。子类通过覆盖这两个方法可以挂钩到对象构建的生命周期中,实现:在配置器(SecurityConfigurer)调用初始化方法或者配置方法之前做用户自定义的操作。
init 方法
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 : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
方法很简单功能很简单,就是遍历 configurers 和 configurersAddedInInitializing ,对里面存储的配置器进行初始化。
配置器初始化的详细内容到看配置器源码时在了解。
configure 方法
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
遍历 configurers
,调用所有配置器的 configure(SecurityBuilder b)
方法对当前的构建器(this)进行配置。
配置器配置详细内容到看配置器源码时在了解。
performBuild 方法
这也是一个抽象方法,需要子类实现,完成对象的创建并返回。
apply 方法
这个方法的作用是将 SecurityConfigurerAdapter (配置器的适配器)或者 SecurityConfigurer (配置器)应用到当前的构建器。这两个方法是相互重载的,他们最后都调用了 add(configurer)
方法,将配置器添加到构建器,方便构建时使用(初始,配置)。
关于 SecurityConfigurerAdapter 和 SecurityConfigurer 后面再详细了解。这里观察可以看出,他们实现了相同的接口,都可以作为add方法的参数。
add 方法
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
这个方法将配置器添加到一个map集合里面,这个map中以配置器的类名为 Key,以存放这个类型的配置器的 List 为 Value。
在执行添加操作时会对 configurers 加锁(synchronized )。
通过构造方法中设置的 allowConfigurersOfSameType 值判断是否允许添加相同类型的配置器,如果是 true ,那么在添加之前会根据类名先从 map 中获取该类型配置器链表(List),如果获取到了就把要添加的配置器追加到后面,然后把追加了新配置器的List再放回到 map 里面,如果获取到 null ,接创建一个新的 List 来存放配置器。
添加配置器时,如果该构建器已经处于以配置状态(大于等于 CONFIGURING.order
),那么会抛出异常;如果该构建器已经处于 INITIALIZING
状态,那么久将这个适配器链表存放到 configurersAddedInInitializing 这个map中;否则将适配器链表存放到 configurers 这个 map 集合中。
遗留一个问题,没有看出来为什么要使用 configurersAddedInInitializing ,如果没有 configurersAddedInInitializing 这个设计会出现什么并发问题吗?
其他方法
剩下的方法都是一些get,set,remove 方法很好理解,不做多余追述。
总结
|- SecurityBuilder
|- AbstractSecurityBuilder
|- AbstractConfiguredSecurityBuilder
SecurityBuilder 接口
功能:构架对象
AbstractSecurityBuilder 抽象类
扩展:
- 对象最多被构建一次
- 定义抽象 doBuild 方法,将构建过程交由子类实现
AbstractConfiguredSecurityBuilder 抽象类
扩展:
- 允许将多个配置器 SecurityConfigurer 应用到该 SecurityBuilder上(同类型配置器可以选择重复或者覆盖)
- 增加内部枚举类BuildState,标记构建过程的生命周期
- 实现了 doBuild 方法,并使用了生命周期
- 增加了 init 和 configure 两个过程的实现,并提供了他们的前置处理挂钩函数给子类使用
- 将构建工作交给抽象方法 performBuild ,交由子类实现
- 为构建过程提供了共享对象