1. 前言
在以往Spring Security的教程中我們自定義配置都是聲明一個配置類WebSecurityConfigurerAdapter
,然後覆寫(@Override
)對應的幾個方法就行了。然而這一切在Spring Security 5.4開始就得到了改變,從Spring Security 5.4 起我們不需要繼承WebSecurityConfigurerAdapter
就可以配置HttpSecurity
了。相關的說明原文:
- Remove need for WebSecurityConfigurerAdapter #8805
- Configure HTTP Security without extending WebSecurityConfigurerAdapter #8804
發稿時最新的Spring Security版本爲 5.4.5。
2. 新的配置方式
舊的配置方式目前依然有效:
@Configuration
static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
);
}
}
不過5.4.x版本我們有新的選擇:
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.antMatcher("/**")
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.build();
}
這種JavaConfig的方式看起來更加清爽舒服,而且和適配器解耦了。等等我好像發現了新的東西,上面filterChain
方法的參數是HttpSecurity
類型。熟悉@Bean
註解的同學應該會意識到一定有一個HttpSecurity
類型的Spring Bean。沒錯!就在HttpSecurityConfiguration
中有一個這樣的Bean:
@Bean({"org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity"})
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
// 省略掉
return http;
}
初始化的內容已經忽略掉,它不是本文關注的重點。我們注意到HttpSecurity
被@Scope("prototype")
標記。也就是這個HttpSecurity
Bean不是單例的,每一次請求都會構造一個新的實例。這個設定非常方便我們構建多個互相沒有太多關聯的SecurityFilterChain
,進而能在一個安全體系中構建相互隔離的安全策略。比如後端管理平臺用Session模式,前臺應用端用Token模式。
3. 原理
Spring Security 有一個名爲springSecurityFilterChain
默認的過濾器鏈類(實際位置就是上圖的 Bean Filter位置),其類型爲FilterChainProxy
, 它代理了所有的SecurityFilterChain
,關鍵的代理注入代碼:
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
那麼this.securityFilterChains
來自哪裏呢?
@Autowired(required = false)
void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
securityFilterChains.sort(AnnotationAwareOrderComparator.INSTANCE);
this.securityFilterChains = securityFilterChains;
}
到這裏就一目瞭然了吧,SecurityFilterChain
類型的Bean會被加載到this.securityFilterChains
中。如果你的Spring Security 版本升級到 5.4.x**,就可以嘗試一下這種方式。
關注公衆號:Felordcn獲取更多資訊