Spring Security 實戰乾貨:5.4版本帶來的新玩法

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模式。

多個SecurityFilterChain

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獲取更多資訊

個人博客:https://felord.cn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章