共计 9698 个字符,预计需要花费 25 分钟才能阅读完成。
咱们持续来撸 Spring Security 源码,明天来撸一个十分重要的 WebSecurityConfigurerAdapter。
咱们的自定义都是继承自 WebSecurityConfigurerAdapter 来实现的,然而对于 WebSecurityConfigurerAdapter 外部的工作原理,配置原理,很多小伙伴可能都还不太熟悉,因而咱们明天就来捋一捋。
咱们先来看一张 WebSecurityConfigurerAdapter 的继承关系图:
在这层继承关系中,有两个十分重要的类:
- SecurityBuilder
- SecurityConfigurer
这两个类松哥在之前的文章中都和大家分享过了,具体参考:
- 深刻了解 HttpSecurity【源码篇】(本文讲的是 SecurityBuilder 体系)
- 深刻了解 SecurityConfigurer【源码篇】
所以对于这两个类的介绍以及作用,松哥这里就不赘述了。咱们间接从 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<Filter>,依据 WebSecurityConfigurerAdapter 中的定义,咱们能够晓得,T 就是 WebSecurity,咱们也大略能猜出 WebSecurity 就是 SecurityBuilder<Filter> 的子类。
所以 WebSecurityConfigurer 的目标咱们能够了解为就是为了配置 WebSecurity。
2.WebSecurity
咱们来看下 WebSecurity 的定义:
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {}
没错,的确是这样!WebSecurity 继承自 AbstractConfiguredSecurityBuilder<Filter, WebSecurity> 同时实现了 SecurityBuilder<Filter> 接口。
WebSecurity 的这些接口和继承类,松哥在后面的源码剖析中都和大家介绍过了,可能有的小伙伴遗记了,我再来和大家温习一下。
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 中的办法比拟多,松哥在这里列出来两个要害的办法和大家剖析:
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;
}
第一个就是这个 add 办法,这相当于是在收集所有的配置类。将所有的 xxxConfigure 收集起来存储到 configurers 中,未来再对立初始化并配置,configurers 自身是一个 LinkedHashMap,key 是配置类的 class,value 是一个汇合,汇合里边放着 xxxConfigure 配置类。当须要对这些配置类进行集中配置的时候,会通过 getConfigurers 办法获取配置类,这个获取过程就是把 LinkedHashMap 中的 value 拿进去,放到一个汇合中返回。
另一个办法就是 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);
}
}
在 AbstractSecurityBuilder 类中,过滤器的构建被转移到 doBuild 办法下面了,不过在 AbstractSecurityBuilder 中只是定义了形象的 doBuild 办法,具体的实现在 AbstractConfiguredSecurityBuilder。
doBuild 办法就是一边更新状态,进行进行初始化。
beforeInit 是一个预留办法,没有任何实现。
init 办法就是找到所有的 xxxConfigure,挨个调用其 init 办法进行初始化。
beforeConfigure 是一个预留办法,没有任何实现。
configure 办法就是找到所有的 xxxConfigure,挨个调用其 configure 办法进行配置。
最初则是 performBuild 办法,是真正的过滤器链构建办法,然而在 AbstractConfiguredSecurityBuilder 中 performBuild 办法只是一个形象办法,具体的实现在它的子类中,也就是 WebSecurityConfigurer。
SecurityBuilder<Filter>
SecurityBuilder 就是用来构建过滤器链的,在 HttpSecurity 实现 SecurityBuilder 时,传入的泛型就是 DefaultSecurityFilterChain,所以 SecurityBuilder#build 办法的性能很明确,就是用来构建一个过滤器链进去,然而那个过滤器链是 Spring Security 中的。在 WebSecurityConfigurerAdapter 中定义的泛型是 SecurityBuilder<Filter>,所以最终构建的是一个一般 Filter,其实就是 FilterChainProxy,对于 FilterChainProxy,大家能够参考深刻了解 FilterChainProxy【源码篇】。
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,如果你还不理解什么是 FilterChainProxy,能够参考松哥之前的介绍:深刻了解 FilterChainProxy【源码篇】。
把握住了这条主线,咱们再来看办法的实现就很容易了。
- 首先统计过滤器链的总条数,总条数包含两个方面,一个是 ignoredRequests,这是疏忽的申请,通过 WebSecurity 配置的疏忽申请,松哥之前介绍过,参见:Spring Security 两种资源放行策略,千万别用错了!,另一个则是 securityFilterChainBuilders,也就是咱们通过 HttpSecurity 配置的过滤器链,有几个就算几个。
- 创立 securityFilterChains 汇合,并且遍历下面提到的两种类型的过滤器链,并将过滤器链放入 securityFilterChains 汇合中。
- 我在深刻了解 HttpSecurity【源码篇】一文中介绍过,HttpSecurity 构建进去的过滤器链对象就是 DefaultSecurityFilterChain,所以能够间接将 build 后果放入 securityFilterChains 中,而 ignoredRequests 中保留的则须要重构一下才能够存入 securityFilterChains。
- securityFilterChains 中有数据之后,接下来创立一个 FilterChainProxy。
- 给新建的 FilterChainProxy 配置上防火墙,防火墙的介绍参考松哥之前的:Spring Security 自带防火墙!你都不晓得本人的零碎有多平安!。
- 最初咱们返回的就是 FilterChainProxy 的实例。
从这段剖析中,咱们能够看进去 WebSecurity 和 HttpSecurity 的区别:
- HttpSecurity 目标是构建过滤器链,一个 HttpSecurity 对象构建一条过滤器链,一个过滤器链中有 N 个过滤器,HttpSecurity 所做的事件实际上就是在配置这 N 个过滤器。
- WebSecurity 目标是构建 FilterChainProxy,一个 FilterChainProxy 中蕴含有多个过滤器链和一个 Firewall。
这就是 WebSecurity 的次要作用,外围办法是 performBuild,其余办法都比较简单,松哥就不一一解释了。
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) 办法,在松哥本系列后面的文章中,configure(http) 办法简直都有重写。HttpSecurity 配置实现后,再将 HttpSecurity 放入 WebSecurity 中,保留在 WebSecurity 的 securityFilterChainBuilders 汇合里,具体参见:深刻了解 HttpSecurity【源码篇】。
configure(WebSecurity web) 办法实际上是一个空办法,咱们在理论开发中可能会重写该办法(参见 Spring Security 两种资源放行策略,千万别用错了!一文):
public void configure(WebSecurity web) throws Exception {}
4. 小结
这便是 WebSecurityConfigurerAdapter,整体上来说并不难,然而要和松哥后面几篇源码剖析文章一起看,了解会更加粗浅一些。
传送门:
- 深刻了解 FilterChainProxy【源码篇】
- 深刻了解 SecurityConfigurer【源码篇】
- 深刻了解 HttpSecurity【源码篇】
- 深刻了解 AuthenticationManagerBuilder【源码篇】
好啦,小伙伴们要是有播种,记得点个在看激励下松哥哦~