Spring Security 過濾器註冊脈絡梳理
1 簡述
Spring Security 本質上就是通過一系列的過濾器,進行業務的處理。
Spring Security
在 Servlet
的過濾鏈(filter chain
)中註冊瞭一個過濾器 FilterChainProxy
,它會把請求代理到 Spring Security
自己維護的多個過濾鏈,每個過濾鏈會匹配一些 URL
,如果匹配則執行對應的過濾器。過濾鏈是有順序的,一個請求隻會執行第一條匹配的過濾鏈。Spring Security
的配置本質上就是新增、刪除、修改過濾器
但是萬物終歸有源頭,過濾器是如何註冊進來的,通過過程瞭解註冊的骨架。
註明 這裡隻是使用瞭 spring web + spring security,同時使用 web.xml 的風格進行配置,如果你使用 SPI 機制(沒有使用 web.xml),殊途同歸。
2 註冊過程
2.1 web.xml 配置
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
其中 DelegatingFilterProxy
類圖如下:
根據類圖看出,DelegatingFilterProxy
繼承 GenericFilterBean
,其間接實現瞭 Filter
接口,所以從類型上看,其也是一個過濾器。
從 Spring 容器中尋找 targetBeanName=springSecurityFilterChain 的 Bean, 從 DelegatingFilterProxy 的名字上看,知道它其實是一個代理類,真正執行業務的,是 filter-name 指定的 springSecurityFilterChain 這個 bean。接下來,問題來瞭,springSecurityFilterChain 這個 bean 又是在什麼時候註冊的呢
2.2 EnableWebSecurity 註解
我們一般在使用 Spring Security
的時候,都會自定義繼承 WebSecurityConfigurerAdapter
,同時結合使用 EnableWebSecurity
註解,然後在自定義的類中進行各種各樣滿足業務的工作。
@EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ... }
這裡我們重點關註的是
EnableWebSecurity
註解,查看下其源碼,做瞭兩個非常重要的點
- 1 導入
WebSecurityConfiguration
配置。 - 2 通過
@EnableGlobalAuthentication
註解引入全局配置
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class }) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { /** * Controls debugging support for Spring Security. Default is false. * @return if true, enables debug support with Spring Security */ boolean debug() default false; }
此註解中由使用瞭
Import
註解引入瞭其他的bean
,然後查看其源碼,重點關註WebSecurityConfiguration
。
2.3 WebSecurityConfiguration 類
這個類裡面比較重要的就兩個方法:
1 springSecurityFilterChain
springSecurityFilterChain
方法上添加瞭 @Bean
註解,可以知道是創建瞭springSecurityFilterChain
bean
2 setFilterChainProxySecurityConfigurer
這個方式設置瞭對應的配置,註意,這個方法優先上面的方法執行。
分析其重點代碼
private WebSecurity webSecurity; // 註入 bean @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty(); boolean hasFilterChain = !this.securityFilterChains.isEmpty(); ... 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(); } @Autowired(required = false) public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor)); if (this.debugEnabled != null) { this.webSecurity.debug(this.debugEnabled); } webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE); Integer previousOrder = null; Object previousConfig = null; for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) { Integer order = AnnotationAwareOrderComparator.lookupOrder(config); if (previousOrder != null && previousOrder.equals(order)) { throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order + " was already used on " + previousConfig + ", so it cannot be used on " + config + " too."); } previousOrder = order; previousConfig = config; } for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { this.webSecurity.apply(webSecurityConfigurer); } this.webSecurityConfigurers = webSecurityConfigurers; }
我們先看執行的 setFilterChainProxySecurityConfigurer
方法,其中參數 webSecurityConfigurers
是一個 List
,它實際上是所有 WebSecurityConfigurerAdapter
的子類,那如果我們定義瞭自定義的配置類,也意味著讀取瞭我們自定義的類。
接著看到 springSecurityFilterChain
方法註冊瞭一個名字為 AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME 的 bean
,翻看源碼,其實名字就是在 web.xml
中配置的 filter-name
,即 springSecurityFilterChain
,到這裡困惑解開瞭一部分,原來查詢的 bean 是在這裡註入的。同時也要求瞭在使用 spring-security
時,在 web.xml
中配置 filter
時,不能是其他名字。
根據
this.webSecurity.build
這行代碼,發現真正構建過濾器的是WebSecurity
類
2.4 WebSecurity 類
接上一步中的代碼
// 重點關註 return this.webSecurity.build();
知道起作用的是 WebSecurity 類,其類圖結構如下:
根據源碼,定位到 WebSecurity
類中的 performBuild
方法
@Override protected Filter performBuild() throws Exception { ... int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize); for (RequestMatcher ignoredRequest : this.ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } // ① 調用 securityFilterChainBuilder 的 build() 方法 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; ... this.postBuildAction.run(); return result; }
代碼分析:
- 1 代碼中的
①
處,重點關註,通過斷點調試,發現其實這裡的securityFilterChainBuilder
就是HttpSecurity
,到這裡也很清楚瞭,默認以及自定義過濾器是通過HttpSecurity
加載進來的。 - 2 創建好過濾器鏈之後,然後實例化
filterChainProxy
進行返回
根據源碼知道
performBuild
方法最終返回的其實是一個filterChainProxy
實例,接下來我們再關註下filterChainProxy
類。
2.5 FilterChainProxy 類
其類圖結構如下
查看其關鍵代碼:
public class FilterChainProxy extends GenericFilterBean { // 維護的 spring security 過濾器鏈列表 private List<SecurityFilterChain> filterChains; public FilterChainProxy(SecurityFilterChain chain) { this(Arrays.asList(chain)); } public FilterChainProxy(List<SecurityFilterChain> filterChains) { this.filterChains = filterChains; } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean clearContext = request.getAttribute(FILTER_APPLIED) == null; if (!clearContext) { doFilterInternal(request, response, chain); return; } try { request.setAttribute(FILTER_APPLIED, Boolean.TRUE); // 真正起作用的函數 doFilterInternal(request, response, chain); } catch (RequestRejectedException ex) { this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex); } ... } private void doFilterInternal( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest( (HttpServletRequest) request); HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse( (HttpServletResponse) response); List<Filter> filters = getFilters(firewallRequest); if (filters == null || filters.size() == 0) { ... chain.doFilter(firewallRequest, firewallResponse); return; } VirtualFilterChain virtualFilterChain = new VirtualFilterChain( firewallRequest, chain, filters); virtualFilterChain.doFilter(firewallRequest, firewallResponse); } }
通過代碼分析,FilterChainProxy
這個類維護瞭真正的過濾器鏈列表,即 SecurityFilterChain
類型的過濾器鏈,註意,SecurityFilterChain
是過濾器鏈,而不是一個個的過濾器,過濾器會有其他的操作塞到過濾器鏈中,即 2.4
中的代碼塊,代碼如下。
for (RequestMatcher ignoredRequest : this.ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } // ① 調用 securityFilterChainBuilder 的 build() 方法 for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); }
這段代碼其實執行的業務就是把過濾器塞入到過濾器鏈中。
這裡也更加清楚瞭, Spring Security Filter
並不是直接嵌入到 Web Filter
中的,而是通過 FilterChainProxy
來統一管理 Spring Security Filter
,FilterChainProxy
本身則通過 Spring
提供的 DelegatingFilterProxy
代理過濾器嵌入到 Web Filter
之中。
3 小結
根據趟源碼,大概瞭解瞭過濾器註冊的流程:
- 1
web.xml
中DelegatingFilterProxy
配置 - 2
EnableWebSecurity
註解引入配置初始化 - 3
WebSecurityConfiguration
類註入springSecurityFilterChain
的bean
並開始構建過濾器 - 4
WebSecurity
類中的performBuild
方法把過濾器都處理好,放到過濾器鏈中,接著實例化filterChainProxy
,這裡實例化返回的對象就是第三步的過濾器
到此這篇關於Spring Security 過濾器註冊脈絡梳理的文章就介紹到這瞭,更多相關Spring Security 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Spring Security過濾器鏈加載執行流程源碼解析
- Spring Security的過濾器鏈機制
- Spring Boot security 默認攔截靜態資源的解決方法
- Spring Security和自定義filter的沖突導致多執行的解決方案
- Idea中如何查看SpringSecurity各Filter信息