一、前言
本章主要内容是关于配置器的接口架构设计,任意找一个配置器一直往上找,就会找到配置器的顶级接口:SecurityConfigurer
。
查看SecurityConfigurer
接口的实现类情况:
在
AbstractHttpConfigurer
抽象类的下面可以看到所有用来配置HttpSecurity
的配置器实现类(也是构造器)。
再通过继承关系图,看看配置器顶层的架构:
会发现,其中:SecurityConfigurerAdapter
和GlobalAuthenticationConfigurerAdapter
对SecurityConfigurer
接口进行了实现,而WebSecurityConfigurer
对SecurityConfigurer
接口进行了继承。
查看SecurityConfigurer
源码:
源码注释:
Allows for configuring a SecurityBuilder. All SecurityConfigurer first have their init(SecurityBuilder) method invoked. After all init(SecurityBuilder) methods have been invoked, each configure(SecurityBuilder) method is invoked.
允许配置构造器SecurityBuilder。所有SecurityConfigurer首先调用其init(SecurityBuilderr) 方法。在调用了所有init(SecurityBuilderr) 方法之后,将调用每个configure(SecurityBuilderr) 方法。
请参阅:
AbstractConfiguredSecurityBuilder
作者:
Rob Winch
类型形参:
<O>
– The object being built by the SecurityBuilder B 由构造器SecurityBuilder<B>
构造出最终目的O
类型对象
<B>
– The SecurityBuilder that builds objects of type O. This is also the SecurityBuilder that is being configured 构造出O
类型对象的构造器SecurityBuilder<B>
,也是正在配置的构造器。
特殊说明:
SecurityConfigurer
的所有实现类都是用来配置构造器的。也就是说,泛型中 O 和 B 的关系是,B 用来构造 O。而配置器的作用是配置这个构造器的,从而影响最终构造的结果。
/**
* Allows for configuring a {@link SecurityBuilder}. All {@link SecurityConfigurer} first
* have their {@link #init(SecurityBuilder)} method invoked. After all
* {@link #init(SecurityBuilder)} methods have been invoked, each
* {@link #configure(SecurityBuilder)} method is invoked.
*
* @param <O> The object being built by the {@link SecurityBuilder} B
* @param <B> The {@link SecurityBuilder} that builds objects of type O. This is also the
* {@link SecurityBuilder} that is being configured.
* @author Rob Winch
* @see AbstractConfiguredSecurityBuilder
*/
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.
*
* 初始化构造器B。在这里只应创建和修改共享状态,而不应在用于构造器B上创建和修改属性。
* 这确保了configure(SecurityBuilder)方法在构造时使用正确的共享对象。
* 应在此处应用配置程序。
*
* @param builder
* @throws Exception
*/
void init(B builder) throws Exception;
/**
* Configure the {@link SecurityBuilder} by setting the necessary properties on the
*
* 通过在构造器B上设置必要的属性来配置构造器B。
*
* {@link SecurityBuilder}.
* @param builder
* @throws Exception
*/
void configure(B builder) throws Exception;
}
下面我们分别对SecurityConfigurerAdapter
、GlobalAuthenticationConfigurerAdapter
、WebSecurityConfigurer
三个分支进行源码分析。
二、SecurityConfigurerAdapter
源码注释:
A base class for SecurityConfigurer that allows subclasses to only implement the methods they are interested in. It also provides a mechanism for using the SecurityConfigurer and when done gaining access to the SecurityBuilder that is being configured.
SecurityConfigurer
的基类,它允许子类仅实现它们感兴趣的方法。它还提供了配置SecurityConfigurer
完成后获取正在配置的构造器SecurityBuilder
的访问机制。
作者:
Rob Winch, Wallace Wadge
类型形参:
<O>
– The Object being built by B 由SecurityBuilder<B>
构造器构造出最终目的O
类型对象
<B>
– The Builder that is building O and is configured by SecurityConfigurerAdapter 由SecurityConfigurerAdapter
配置的构造器SecurityBuilder<B>
正在构造O
/**
* A base class for {@link SecurityConfigurer} that allows subclasses to only implement
* the methods they are interested in. It also provides a mechanism for using the
* {@link SecurityConfigurer} and when done gaining access to the {@link SecurityBuilder}
* that is being configured.
*
* @param <O> The Object being built by B
* @param <B> The Builder that is building O and is configured by
* {@link SecurityConfigurerAdapter}
* @author Rob Winch
* @author Wallace Wadge
*/
public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {
private B securityBuilder;
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
@Override
public void init(B builder) throws Exception {
}
@Override
public void configure(B builder) throws Exception {
}
/**
* Return the {@link SecurityBuilder} when done using the {@link SecurityConfigurer}.
* This is useful for method chaining.
* @return the {@link SecurityBuilder} for further customizations
* @deprecated For removal in 7.0. Use the lambda based configuration instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public B and() {
return getBuilder();
}
/**
* Gets the {@link SecurityBuilder}. Cannot be null.
* @return the {@link SecurityBuilder}
* @throws IllegalStateException if {@link SecurityBuilder} is null
*/
protected final B getBuilder() {
Assert.state(this.securityBuilder != null, "securityBuilder cannot be null");
return this.securityBuilder;
}
/**
* Performs post processing of an object. The default is to delegate to the
* {@link ObjectPostProcessor}.
* @param object the Object to post process
* @return the possibly modified Object to use
*/
@SuppressWarnings("unchecked")
protected <T> T postProcess(T object) {
return (T) this.objectPostProcessor.postProcess(object);
}
/**
* Adds an {@link ObjectPostProcessor} to be used for this
* {@link SecurityConfigurerAdapter}. The default implementation does nothing to the
* object.
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
}
/**
* Sets the {@link SecurityBuilder} to be used. This is automatically set when using
* {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
* @param builder the {@link SecurityBuilder} to set
*/
public void setBuilder(B builder) {
this.securityBuilder = builder;
}
/**
* An {@link ObjectPostProcessor} that delegates work to numerous
* {@link ObjectPostProcessor} implementations.
*
* @author Rob Winch
*/
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object postProcess(Object object) {
for (ObjectPostProcessor opp : this.postProcessors) {
Class<?> oppClass = opp.getClass();
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class);
if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
object = opp.postProcess(object);
}
}
return object;
}
/**
* Adds an {@link ObjectPostProcessor} to use
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
* @return true if the {@link ObjectPostProcessor} was added, else false
*/
private boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
boolean result = this.postProcessors.add(objectPostProcessor);
this.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
return result;
}
}
}
内容很简单:
-
有一个内部类
CompositeObjectPostProcessor
:复合后置处理器对象类 -
定义了两个成员变量:
- 将要配置的构造器
securityBuilder
- 复合后置处理器
objectPostProcessor
- 将要配置的构造器
-
从接口中实现的方法和自己新加的几个方法
init
和configure
是实现接口的方法and
、getBuilder
、postProcess
、addObjectPostProcessor
、setBuilder
方法是自己加的
2.1 允许子类只实现他们感兴趣的方法
在源码中可以看到,所有的方法都有方法体,包括对接口方法的实现(虽然是空方法体)。所以继承 SecurityConfigurerAdapter
的配置器可以根据自己的需求实现(覆盖)自己感兴趣的方法。
可以自己实现
init
,如果自己不需要初始化,也可以不实现,在构造器调用其init
方法时什么也不做。
2.2 setBuilder 方法
这个方法有点特别,所以单独说一下,方法内容很简单,就是设置配置器的成员变量private B securityBuilder
(即:构造器),但是官网又这样一句注释:
Sets the SecurityBuilder to be used.
This is automatically set when using AbstractConfiguredSecurityBuilder.apply(SecurityConfigurerAdapter)
第一句很好理解:这个方法是来设置要使用的构造器(private B securityBuilder
,其B extends SecurityBuilder<O>
)。
第二句就是当 AbstractConfiguredSecurityBuilder.apply(SecurityConfigurerAdapter)
调用时被自动设置。
回顾上篇中的4.4.7章节:
/**
* Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
* invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
* @param configurer
* @return the {@link SecurityConfigurerAdapter} for further customizations
* @throws Exception
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}
可以看出,configurer
在被调用之前是不知道要配置哪个构造器的。在构造器调用 apply
方法时才真正设置配置器的成员变量(即:构造器)。所以官方注释才说 setBuilder
方法时自动调用的,我们不能手动去设置。
只有构造器
(B) this
应用了这个配置器configurer
,这个配置器configurer
才会绑定上这个构造器(B) this
。
2.3 and 方法
and
方法提供了一种使用完配置器后获得正在配置的构造器SecurityBuilder
对象的机制。
有必要说一下为什么是正在配置,因为在这个时候还没有对构造器private B securityBuilder
进行配置。
在上篇文章中讲解Builder
设计模式时,提到过构造器的构造时机在调用构造器的 build
方法,在doBuild
方法中加入了构造生命周期控制,在里面才开始调用各个配置器的 init
方法和 configure
方法。所以这里获取到的时正在配置的构造器private B securityBuilder
对象。
在构造器的构造过程中利用配置器进行配置,从而影响最终构造的结果。
2.4 复合后置处理对象
这个内部类对象很简单,看一下内部类的内容就明白了:它维护的是一个 List
集合
private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
因此称之为:复合后置处理对象(CompositeObjectPostProcessor
),就是里面有多个。
2.5 DEBUG 参数跟踪
AbstractConfiguredSecurityBuilder#apply
:
SecurityConfigurerAdapter#setBuilder
:
由上可知:
SecurityConfigurerAdapter
中设置的构造器为HttpSecurity
。
三、GlobalAuthenticationConfigurerAdapter
这个类的类名说明它是一个全局配置相关的类。
/**
* A {@link SecurityConfigurer} that can be exposed as a bean to configure the global
* {@link AuthenticationManagerBuilder}. Beans of this type are automatically used by
* {@link AuthenticationConfiguration} to configure the global
* {@link AuthenticationManagerBuilder}.
*
* @author Rob Winch
* @since 5.0
*/
@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 {
}
}
从源码可以看出它实现 SecurityConfigurer
接口,没有具体的实现内容,只是对构造器和构造器将要构造的对象做了限制:
-
将要配置的构造器:
AuthenticationManagerBuilder
可以作为
bean
公开以配置全局构造器AuthenticationManagerBuilder
。 -
将要构造的对象:
AuthenticationManager
将被构造器构造出最终目的类型
AuthenticationManager
。
四、WebSecurityConfigurer
/**
* Allows customization to the {@link WebSecurity}. In most instances users will use
* {@link EnableWebSecurity} and create a {@link Configuration} that exposes a
* {@link SecurityFilterChain} bean. This will automatically be applied to the
* {@link WebSecurity} by the {@link EnableWebSecurity} annotation.
*
* @author Rob Winch
* @since 3.2
* @see SecurityFilterChain
*/
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {
}
该接口继承SecurityConfigurer
接口,从源码中可以看出,它没有定义自己的方法,所有的方法都是从父接口继承,那么它的作用还有配置构造器SecurityBuilder
,但是它对类型做了约束:
-
将要输入
WebSecurityConfigurer
的泛型为T
T
继承了SecurityBuilder
,所有T
表示一个对象构建器 (SecurityBuilder
) -
传入
SecurityBuilder
的泛型Filter
(javax.servlet.Filter
)表示
SecurityBuilder
将要创建的对象是一个javax.servet.Filter
,也就是说,它约束了T
,说明T
的作用是构建Filter
-
将要传入父接口
SecurityConfiqurer
的两个泛型:Filter
和T
WebSecurityConfigurer
继承SecurityConfigurer
,从这里可以看出,对SecurityConfigurer
做了约束,目前只有T
还没有指定具体的类型,至于最终使用什么类型的构造器由实现类活着子接口指定。