文章目录
一、WebSecurityConfigurerAdapter是什么?
WebSecurityConfigurerAdapter是为创建WebSecurityConfigurer实例提供方便的基类,该类允许开发人员通过覆盖方法进行定制。
类图:
通过类图,可以了解到WebSecurityConfigurerAdapter实现了WebSecurityConfigurer的接口。
1.WebSecurityConfigurer是什么?
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {
}
WebSecurityConfigurer是一个空的接口,它允许对WebSecurity进行定义,目的就是配置一个WebSecurity。
2.WebSecurity
2.1 WebSecurity定义
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>, ApplicationContextAware {
可以看到WebSecurity继承了AbstractConfiguredSecurityBuilder并实现了SecurityBuilder。
之前分析了解到,WebSecurity由WebSecurityConfiguration创建,以创建称为 Spring Security Filter Chain (springSecurityFilterChain) 的FilterChainProxy 。
2.2 AbstractConfiguredSecurityBuilder
AbstractConfiguredSecurityBuilder维护了每一个子类的实现集合,例如我们探讨的WebSecurity,当我们定义了多个WebSecurityConfigurerAdapter时,会在全局变量configurers属性中维护具体配置。
维护源码:
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 (this.configurers) {
if (this.buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
}
List<SecurityConfigurer<O, B>> configs = null;
if (this.allowConfigurersOfSameType) {
configs = this.configurers.get(clazz);
}
configs = (configs != null) ? configs : new ArrayList<>(1);
configs.add(configurer);
this.configurers.put(clazz, configs);
if (this.buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {
List<C> configs = (List<C>) this.configurers.get(clazz);
if (configs == null) {
return new ArrayList<>();
}
return new ArrayList<>(configs);
}
- add()方法将传入的configer维护到了configers里,configurers 本身是一个 LinkedHashMap ,key 是配置类的 class,value 是一个集合,集合里边放着 xxxConfigure 配置类。
- 当需要对这些配置类进行集中配置的时候,会通过 getConfigurers 方法获取配置类,这个获取过程就是把 LinkedHashMap 中的 value 拿出来,放到一个集合中返回。
AbstractConfiguredSecurityBuilder中还有一个特别重要的方法就是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;
}
}
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
doBuild方法的入口是自动配置WebSecurityConfiguration的springSecurityFilterChain方法:
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
Assert.state(!(hasConfigurers && hasFilterChain),
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
customizer.customize(this.webSecurity);
}
return this.webSecurity.build();
}
此处调用build方法,最终调用的是duBuild方法。
doBuild 方法就是一边更新状态,进行进行初始化。
beforeInit 是一个预留方法,没有任何实现。
init 方法就是找到所有的 xxxConfigure,挨个调用其 init 方法进行初始化。
beforeConfigure 是一个预留方法,没有任何实现。
configure 方法就是找到所有的 xxxConfigure,挨个调用其 configure 方法进行配置。
最后则是 performBuild 方法,是真正的过滤器链构建方法,但是在AbstractConfiguredSecurityBuilder 中 performBuild 方法只是一个抽象方法,具体的实现在它的子类中。
2.3 再次看WebSecurity
通过上面的分析,可以了解到最终调的是子类的performBuild方法,看下WebSecurity的核心逻辑performBuild方法:
@Override
protected Filter performBuild() throws Exception {
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this is done by exposing a SecurityFilterChain bean "
+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (this.debugEnabled) {
this.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);
}
this.postBuildAction.run();
return result;
}
从方法的返回值可以看出,返回的是一个Filter。
- 首先统计过滤器链的总条数,包括ignoredRequests(忽略的请求,通过WebSecurity配置),另外一个是securityFilterChainBuilders(通过HttpSecurity配置的过滤器链)
- 创建securityFilterChains的集合,将上面两个集合分别加入到此securityFilterChains中。
- 再通过securityFilterChains创建FilterChainProxy的实例并设置防火墙和拒绝策略的handler
- 最后返回这个FilterChainProxy实例
3 WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter 中的方法比较多,但是根据我们前面的分析,提纲挈领的方法就两个,一个是 init,还有一个 configure(WebSecurity web),其他方法都是为这两个方法服务的。那我们就来看下这两个方法:
@Override
public void init(WebSecurity web) throws Exception {
HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
protected final HttpSecurity getHttp() throws Exception {
if (this.http != null) {
return this.http;
}
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
if (!this.disableDefaults) {
applyDefaultConfiguration(this.http);
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
this.http.apply(configurer);
}
}
configure(this.http);
return this.http;
}
protected void configure(HttpSecurity http) throws Exception {
this.logger.debug("Using default configure(HttpSecurity). "
+ "If subclassed this will potentially override subclass configure(HttpSecurity).");
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
http.formLogin();
http.httpBasic();
}
init 方法可以算是这里的入口方法了:首先调用 getHttp 方法进行 HttpSecurity 的初始化。HttpSecurity 的初始化,实际上就是配置了一堆默认的过滤器,配置完成后,最终还调用了 configure(http) 方法,该方法又配置了一些拦截器,不过在实际开发中,我们经常会重写 configure(http) 方法。
@Override
public void configure(WebSecurity web) throws Exception {
}
configure(WebSecurity web) 方法实际上是一个空方法,覆盖此方法以配置WebSecurity 。 例如,如果您希望忽略某些请求。 Spring Security 将忽略此方法中指定的端点,这意味着它不会保护它们免受 CSRF、XSS、Clickjacking 等攻击。 相反,如果您想保护端点免受常见漏洞的影响。