Spring?Security?過濾器注冊脈絡(luò)梳理
1 簡述
Spring Security 本質(zhì)上就是通過一系列的過濾器,進行業(yè)務(wù)的處理。
Spring Security 在 Servlet 的過濾鏈(filter chain)中注冊了一個過濾器 FilterChainProxy,它會把請求代理到 Spring Security 自己維護的多個過濾鏈,每個過濾鏈會匹配一些 URL,如果匹配則執(zhí)行對應(yīng)的過濾器。過濾鏈?zhǔn)怯许樞虻?,一個請求只會執(zhí)行第一條匹配的過濾鏈。Spring Security 的配置本質(zhì)上就是新增、刪除、修改過濾器

但是萬物終歸有源頭,過濾器是如何注冊進來的,通過過程了解注冊的骨架。
注明 這里只是使用了 spring web + spring security,同時使用 web.xml 的風(fēng)格進行配置,如果你使用 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 類圖如下:

根據(jù)類圖看出,DelegatingFilterProxy 繼承 GenericFilterBean,其間接實現(xiàn)了 Filter 接口,所以從類型上看,其也是一個過濾器。
從 Spring 容器中尋找 targetBeanName=springSecurityFilterChain 的 Bean, 從 DelegatingFilterProxy 的名字上看,知道它其實是一個代理類,真正執(zhí)行業(yè)務(wù)的,是 filter-name 指定的 springSecurityFilterChain 這個 bean。接下來,問題來了,springSecurityFilterChain 這個 bean 又是在什么時候注冊的呢
2.2 EnableWebSecurity 注解
我們一般在使用 Spring Security 的時候,都會自定義繼承 WebSecurityConfigurerAdapter,同時結(jié)合使用 EnableWebSecurity 注解,然后在自定義的類中進行各種各樣滿足業(yè)務(wù)的工作。
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}這里我們重點關(guān)注的是
EnableWebSecurity注解,查看下其源碼,做了兩個非常重要的點
- 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,然后查看其源碼,重點關(guān)注WebSecurityConfiguration。
2.3 WebSecurityConfiguration 類
這個類里面比較重要的就兩個方法:
1 springSecurityFilterChain
springSecurityFilterChain 方法上添加了 @Bean注解,可以知道是創(chuàng)建了springSecurityFilterChain bean
2 setFilterChainProxySecurityConfigurer
這個方式設(shè)置了對應(yīng)的配置,注意,這個方法優(yōu)先上面的方法執(zhí)行。
分析其重點代碼
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);
}
// 重點關(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,它實際上是所有 WebSecurityConfigurerAdapter 的子類,那如果我們定義了自定義的配置類,也意味著讀取了我們自定義的類。
接著看到 springSecurityFilterChain 方法注冊了一個名字為 AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME 的 bean,翻看源碼,其實名字就是在 web.xml 中配置的 filter-name,即 springSecurityFilterChain,到這里困惑解開了一部分,原來查詢的 bean 是在這里注入的。同時也要求了在使用 spring-security 時,在 web.xml 中配置 filter 時,不能是其他名字。
根據(jù)
this.webSecurity.build這行代碼,發(fā)現(xiàn)真正構(gòu)建過濾器的是WebSecurity類
2.4 WebSecurity 類
接上一步中的代碼
// 重點關(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 代碼中的
①處,重點關(guān)注,通過斷點調(diào)試,發(fā)現(xiàn)其實這里的securityFilterChainBuilder就是HttpSecurity,到這里也很清楚了,默認以及自定義過濾器是通過HttpSecurity加載進來的。 - 2 創(chuàng)建好過濾器鏈之后,然后實例化
filterChainProxy進行返回
根據(jù)源碼知道
performBuild方法最終返回的其實是一個filterChainProxy實例,接下來我們再關(guān)注下filterChainProxy類。
2.5 FilterChainProxy 類
其類圖結(jié)構(gòu)如下

查看其關(guān)鍵代碼:
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);
// 真正起作用的函數(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 這個類維護了真正的過濾器鏈列表,即 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());
}這段代碼其實執(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方法把過濾器都處理好,放到過濾器鏈中,接著實例化filterChainProxy,這里實例化返回的對象就是第三步的過濾器
到此這篇關(guān)于Spring Security 過濾器注冊脈絡(luò)梳理的文章就介紹到這了,更多相關(guān)Spring Security 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實現(xiàn)Excel的導(dǎo)入、導(dǎo)出
這篇文章主要為大家詳細介紹了java實現(xiàn)Excel的導(dǎo)入、導(dǎo)出的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
Spring?Boot實現(xiàn)消息的發(fā)送和接收使用實戰(zhàn)指南
這篇文章主要為大家介紹了Spring?Boot實現(xiàn)消息的發(fā)送和接收使用實戰(zhàn)指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06
AgileBoot?項目內(nèi)統(tǒng)一的錯誤碼設(shè)計分析
這篇文章主要為大家介紹了AgileBoot?項目內(nèi)統(tǒng)一的錯誤碼設(shè)計分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10
Spring中使用JSR303請求約束判空的實現(xiàn)
這篇文章主要介紹了Spring中使用JSR303請求約束判空的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
深入剖析springBoot中的@Scheduled執(zhí)行原理
這篇文章主要介紹了springBoot中的@Scheduled執(zhí)行原理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

