我们自定义的SecurityConfig
都是继承自WebSecurityConfigurerAdapter
,今天我们就来看看WebSecurityConfigurerAdapter
内部的工作原理以及配置原理。WebSecurityConfigurerAdapter
的继承关系图:
在这层继承关系图中,有两个非常重要的类:SecurityBuilder
和SecurityConfigurer
。
这两个类在之前的深入理解Spring Security配置与构建(源码篇)文章中已经介绍过了,所以关于这两个类就不再赘述,直接从WebSecurityConfigurer
类开始。
1、WebSecurityConfigurer
WebSecurityConfigurer
其实是一个空接口,只是它里面约束了一些泛型,如下:
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
SecurityConfigurer<Filter, T> {
}
它里面的泛型很关键,这关乎到WebSecurityConfigurer
的目的是啥!
SecurityBuilder
中的泛型是Filter
,表示SecurityBuilder
最终的目的是为了构建一个Filter
对象出来。SecurityConfigurer
中两个泛型,第一个表示的含义也是SecurityBuilder
最终构建的对象。
同时,这里还定义一个新的泛型T
,T
需要继承自SecurityBuilder
,根据WebSecurityConfigurerAdapter
中的定义,可以知道T
就是SecurityBuilder
的子类WebSecurity
。
所以,WebSecurityConfigurer
的目的可以理解为就是为了配置WebSecurity
。
2、WebSecurity
WebSecurity
定义如下:
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
}
WebSecurity
继承自AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
类同时实现了SecurityBuilder
接口。
其实在前面的源码介绍中就已经介绍过了,我们再来看看。
2.1、AbstractConfiguredSecurityBuilder
首先,AbstractConfiguredSecurityBuilder
中定义了一个枚举类,将整个构建过程分为5种状态,也可以理解为构建过程生命周期的五个阶段,如下:
private enum BuildState {
UNBUILT(0),
INITIALIZING(1),
CONFIGURING(2),
BUILDING(3),
BUILT(4);
private final int order;
BuildState(int order) {
this.order = order;
}
public boolean isInitializing() {
return INITIALIZING.order == order;
}
public boolean isConfigured() {
return order >= CONFIGURING.order;
}
}
五种状态分别是UNBUILT``INITIALIZING``CONFIGURING``BUILDING
以及BUILT
。另外还提供了两个判断方法,isInitializing
判断是否正在初始化阶段,isConfigured
表示是否已经构建完成。AbstractConfiguredSecurityBuilder
中的方法比较多,在这里只列出两个关键的方法:
第一个方法就是这个
add
方法,这相当于在收集所有的配置。将所有的xxxConfigurer
收集起来存储到configurers
中,将来再统一初始化并配置。configurers
本身是一个LinkedHashMap
,key 是配置类的 class,value 是xxxConfigurer
配置类集合。当需要对这些配置类进行集中配置的时候,会通过getConfigurers
方法获取配置类集合,这个获取过程就是就是把LinkedHashMap
中的 value 拿出来,放到一个集合中返回。private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
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<>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
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
方法。在AbstractSecurityBuilder
类中,构建的核心逻辑被放到doBuild
方法上,但是AbstractSecurityBuilder
类中只是定义了抽象的doBuild
方法,真正的实现还是在该类中的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;
}
}
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);
}
}
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
doBuild
方法就是一边更新状态一边进行构建。beforeInit
是一个预留方法,没有任何实现;init
方法就是找到所有的xxxConfigurer
,挨个调用其init
方法进行初始化;beforeConfigure
是一个预留方法,没有任何实现;configure
方法就是找到所有的xxxConfigurer
,挨个调用其configure
方法进行配置;performBuild
方法是真正的过滤器链构建方法,但是在当前AbstractConfiguredSecurityBuilder
中的performBuild
方法只是一个抽象方法,具体的实现在HttpSecurity
类中。2.2、SecurityBuilder
在
HttpSecurity
实现SecurityBuilder
时,传入的泛型是DefaultSecurityFilterChain
,所以SecurityBuilder#build
方法的功能就很明确,就是用来构建一个过滤器链出来,但是那个过滤器链是 Spring Security 中的。在WebSecurityConfigurerAdapter
中定义的泛型是WebSecurity
,所以最终构建的是一个普通的Filter
,其实就是FilterChainProxy
,关于FilterChainProxy
,可以参考Spring Security过滤器架构(源码篇)文章。2.3、WebSecurity
WebSecurity
的核心逻辑集中在performBuild
构建方法上:@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
这里的
performBuild
方法只有一个功能,那就是构建FilterChainProxy
。首先,统计过滤器链的总数,总条数包括两个方面,一个是
ignoredRequests
,这是忽略的请求,通过WebSecurity
配置的忽略请求;另一个则是securityFilterChainBuilders
,也就是通过HttpSecurity
配置的过滤器链,有几个算几个。- 创建
securityFilterChains
集合,并且遍历上面提到的两种类型的过滤器链,并将过滤器链放入securityFilterChains
集合中。 - 在前面的深入理解Spring Security配置与构建(源码篇)文章中介绍过,
HttpSecurity
构建出来的过滤器链对象就是DefaultSecurityFilterChain
,所以可以直接将build
结果放入securityFilterChains
集合中,而ignoredRequests
中保存时则需要包装一下才可以存入securityFilterChains
中。 securityFilterChains
集合有数据之后,创建一个FilterChainProxy
。- 给新建的
FilterChainProxy
配置上防火墙。 - 最后我们返回的就是
FilterChainProxy
实例。
🎯从这段分析中,可以看出WebSecurity
和HttpSecurity
的区别:
HttpSecurity
目的是构建过滤器链,一个HttpSecurity
对象构建一条过滤器链,一个过滤器链中有N
多个过滤器,HttpSecurity
所做的事情实际上就是在配置这N
个过滤器。WebSecurity
目的是构建FilterChainProxy
,一个FilterChainProxy
中包含有多个过滤器链和一个Firewall
。3、WebSecurityConfigurerAdapter
我们最后来看看WebSecurityConfigurerAdapter
,由于WebSecurityConfigurer
只是一个空接口,WebSecurityConfigurerAdapter
就是针对这个空接口提供一个具体的实现,最终目的还是为了方便你配置WebSecurity
。WebSecurityConfigurerAdapter
中的方法比较多,但是根据我们前面这么文章的分析,重要的方法就两个,一个是init
方法,另一个就是configure(WebSecurity web)
,其他方法都是为这两个方法服务的。
先看init
方法:public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
init
方法可以算是这里的入口方法了:首先调用getHttp
方法进行HttpSecurity
的初始化。HttpSecurity
的初始化,实际上就是配置了一堆默认的过滤器,配置完成后,最终还是调用了configure(http)
方法,该方法又配置了一些过滤器,不过在实际的开发中,我们经常会重写configure(http)
方法。HttpSecurity
配置完成后,再将HttpSecurity
放入WebSecurity
的securityFilterChainBuilders
集合中。
configure(WebSecurity web)
方法实际上是一个空方法,我们在实际开发中可能会重写该方法:
public void configure(WebSecurity web) throws Exception {
}