Spring?Security?過濾器注冊脈絡(luò)梳理
1 簡述
Spring Security 本質(zhì)上就是通過一系列的過濾器,進(jìn)行業(yè)務(wù)的處理。
Spring Security
在 Servlet
的過濾鏈(filter chain
)中注冊了一個過濾器 FilterChainProxy
,它會把請求代理到 Spring Security
自己維護(hù)的多個過濾鏈,每個過濾鏈會匹配一些 URL
,如果匹配則執(zhí)行對應(yīng)的過濾器。過濾鏈?zhǔn)怯许樞虻?,一個請求只會執(zhí)行第一條匹配的過濾鏈。Spring Security
的配置本質(zhì)上就是新增、刪除、修改過濾器
但是萬物終歸有源頭,過濾器是如何注冊進(jìn)來的,通過過程了解注冊的骨架。
注明 這里只是使用了 spring web + spring security,同時使用 web.xml 的風(fēng)格進(jìn)行配置,如果你使用 SPI 機(jī)制(沒有使用 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
類圖如下:
根據(jù)類圖看出,DelegatingFilterProxy
繼承 GenericFilterBean
,其間接實(shí)現(xiàn)了 Filter
接口,所以從類型上看,其也是一個過濾器。
從 Spring 容器中尋找 targetBeanName=springSecurityFilterChain 的 Bean, 從 DelegatingFilterProxy 的名字上看,知道它其實(shí)是一個代理類,真正執(zhí)行業(yè)務(wù)的,是 filter-name 指定的 springSecurityFilterChain 這個 bean。接下來,問題來了,springSecurityFilterChain 這個 bean 又是在什么時候注冊的呢
2.2 EnableWebSecurity 注解
我們一般在使用 Spring Security
的時候,都會自定義繼承 WebSecurityConfigurerAdapter
,同時結(jié)合使用 EnableWebSecurity
注解,然后在自定義的類中進(jìn)行各種各樣滿足業(yè)務(wù)的工作。
@EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ... }
這里我們重點(diǎn)關(guān)注的是
EnableWebSecurity
注解,查看下其源碼,做了兩個非常重要的點(diǎn)
- 1 導(dǎo)入
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
,然后查看其源碼,重點(diǎn)關(guān)注WebSecurityConfiguration
。
2.3 WebSecurityConfiguration 類
這個類里面比較重要的就兩個方法:
1 springSecurityFilterChain
springSecurityFilterChain
方法上添加了 @Bean
注解,可以知道是創(chuàng)建了springSecurityFilterChain
bean
2 setFilterChainProxySecurityConfigurer
這個方式設(shè)置了對應(yīng)的配置,注意,這個方法優(yōu)先上面的方法執(zhí)行。
分析其重點(diǎn)代碼
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); } // 重點(diǎn)關(guān)注 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; }
我們先看執(zhí)行的 setFilterChainProxySecurityConfigurer
方法,其中參數(shù) webSecurityConfigurers
是一個 List
,它實(shí)際上是所有 WebSecurityConfigurerAdapter
的子類,那如果我們定義了自定義的配置類,也意味著讀取了我們自定義的類。
接著看到 springSecurityFilterChain
方法注冊了一個名字為 AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME 的 bean
,翻看源碼,其實(shí)名字就是在 web.xml
中配置的 filter-name
,即 springSecurityFilterChain
,到這里困惑解開了一部分,原來查詢的 bean 是在這里注入的。同時也要求了在使用 spring-security
時,在 web.xml
中配置 filter
時,不能是其他名字。
根據(jù)
this.webSecurity.build
這行代碼,發(fā)現(xiàn)真正構(gòu)建過濾器的是WebSecurity
類
2.4 WebSecurity 類
接上一步中的代碼
// 重點(diǎn)關(guān)注 return this.webSecurity.build();
知道起作用的是 WebSecurity 類,其類圖結(jié)構(gòu)如下:
根據(jù)源碼,定位到 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)); } // ① 調(diào)用 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 代碼中的
①
處,重點(diǎn)關(guān)注,通過斷點(diǎn)調(diào)試,發(fā)現(xiàn)其實(shí)這里的securityFilterChainBuilder
就是HttpSecurity
,到這里也很清楚了,默認(rèn)以及自定義過濾器是通過HttpSecurity
加載進(jìn)來的。 - 2 創(chuàng)建好過濾器鏈之后,然后實(shí)例化
filterChainProxy
進(jìn)行返回
根據(jù)源碼知道
performBuild
方法最終返回的其實(shí)是一個filterChainProxy
實(shí)例,接下來我們再關(guān)注下filterChainProxy
類。
2.5 FilterChainProxy 類
其類圖結(jié)構(gòu)如下
查看其關(guān)鍵代碼:
public class FilterChainProxy extends GenericFilterBean { // 維護(hù)的 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); // 真正起作用的函數(shù) 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
這個類維護(hù)了真正的過濾器鏈列表,即 SecurityFilterChain
類型的過濾器鏈,注意,SecurityFilterChain
是過濾器鏈,而不是一個個的過濾器,過濾器會有其他的操作塞到過濾器鏈中,即 2.4
中的代碼塊,代碼如下。
for (RequestMatcher ignoredRequest : this.ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } // ① 調(diào)用 securityFilterChainBuilder 的 build() 方法 for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); }
這段代碼其實(shí)執(zhí)行的業(yè)務(wù)就是把過濾器塞入到過濾器鏈中。
這里也更加清楚了, Spring Security Filter
并不是直接嵌入到 Web Filter
中的,而是通過 FilterChainProxy
來統(tǒng)一管理 Spring Security Filter
,FilterChainProxy
本身則通過 Spring
提供的 DelegatingFilterProxy
代理過濾器嵌入到 Web Filter
之中。
3 小結(jié)
根據(jù)趟源碼,大概了解了過濾器注冊的流程:
- 1
web.xml
中DelegatingFilterProxy
配置 - 2
EnableWebSecurity
注解引入配置初始化 - 3
WebSecurityConfiguration
類注入springSecurityFilterChain
的bean
并開始構(gòu)建過濾器 - 4
WebSecurity
類中的performBuild
方法把過濾器都處理好,放到過濾器鏈中,接著實(shí)例化filterChainProxy
,這里實(shí)例化返回的對象就是第三步的過濾器
到此這篇關(guān)于Spring Security 過濾器注冊脈絡(luò)梳理的文章就介紹到這了,更多相關(guān)Spring Security 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中關(guān)于內(nèi)存泄漏出現(xiàn)的原因匯總及如何避免內(nèi)存泄漏(超詳細(xì)版)
這篇文章主要介紹了Java中關(guān)于內(nèi)存泄漏出現(xiàn)的原因匯總及如何避免內(nèi)存泄漏(超詳細(xì)版)的相關(guān)資料,需要的朋友可以參考下2016-09-09SSH框架網(wǎng)上商城項(xiàng)目第21戰(zhàn)之詳解易寶支付的流程
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第21戰(zhàn)之易寶支付的流程,感興趣的小伙伴們可以參考一下2016-06-06MyBatis-Plus 自定義sql語句的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis-Plus 自定義sql語句的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java享元設(shè)計(jì)模式優(yōu)化對象創(chuàng)建提高性能和效率
Java享元設(shè)計(jì)模式通過共享可重用的對象,減少了系統(tǒng)中對象的數(shù)量,優(yōu)化了對象的創(chuàng)建和管理,提高了性能和效率。它是一種經(jīng)典的設(shè)計(jì)模式,適用于需要處理大量相似對象的應(yīng)用程序2023-04-04Java?Bean轉(zhuǎn)Map的那些踩坑實(shí)戰(zhàn)
項(xiàng)目中有時會遇到Map轉(zhuǎn)Bean,Bean轉(zhuǎn)Map的情況,下面這篇文章主要給大家介紹了關(guān)于Java?Bean轉(zhuǎn)Map那些踩坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07