Spring?Security過(guò)濾器鏈加載執(zhí)行流程源碼解析
Spring Security實(shí)現(xiàn)原理
Spring Security 采用 IoC 和 AOP思想,基于 Servlet 過(guò)濾器實(shí)現(xiàn)的安全框架、為 Web 請(qǐng)求和方法調(diào)用提供身份確認(rèn)和授權(quán)處理,還提供與其他庫(kù)的集成以簡(jiǎn)化其使用,避免了代碼耦合,減少了大量重復(fù)代碼工作。
在之前 web.xml中,我們是這樣寫的。
<!--SpringSecurity核心過(guò)濾器鏈--> <!--springSecurityFilterChain名詞不能修改--> <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>
在 Spring Boot項(xiàng)目之后,我們引入 Spring Security依賴,什么也沒做,啟動(dòng)項(xiàng)目 Spring Security 就會(huì)生效,訪問(wèn)請(qǐng)求就進(jìn)行了攔截。
Spring Boot 對(duì)于 Spring Security 提供了自動(dòng)化配置方案,可以使用更少的配置來(lái)使用 Spring Security。
那么這個(gè)過(guò)濾器鏈?zhǔn)窃趺醇虞d和實(shí)現(xiàn)攔截的呢?
一、Spring Security過(guò)濾器鏈加載
1、注冊(cè)名為 springSecurityFilterChain的過(guò)濾器
當(dāng) Spring Boot 項(xiàng)目啟動(dòng)后,SecurityFilterAutoConfiguration類會(huì)加載 DelegatingFilterProxyRegistrationBean注冊(cè)過(guò)濾器,名字為 springSecurityFilterChain。
注意:
springSecurityFilterChain名字是固定寫死的。
DelegatingFilterProxyRegistrationBean 注冊(cè)成功后,該過(guò)濾器就被加載了到了注冊(cè)器中。
注冊(cè)器注冊(cè)了所有的過(guò)濾器后,會(huì)為每個(gè)過(guò)濾器生成 DelegatingFilterProxy代理對(duì)象并注冊(cè)到 IoC中 。
2、查看 DelegatingFilterProxy類
我們?cè)L問(wèn)項(xiàng)目,就會(huì)進(jìn)入 DelegatingFilterProxy類的 doFilter方法。
DelegatingFilterProxy類
本質(zhì)也是一個(gè) Filter,其間接實(shí)現(xiàn)了 Filter接口,但是在 doFilter中其實(shí)調(diào)用的從 Spring 容器中獲取到的代理 Filter的實(shí)現(xiàn)類。
返回的 FilterChainProxy對(duì)象。
由此可知,DelegatingFilterProxy類通過(guò) springSecurityFilterChain這個(gè)名稱,得到了一個(gè) FilterChainProxy過(guò)濾器,最終執(zhí)行的是這個(gè)過(guò)濾器的 doFilter方法。
1)驗(yàn)證 springSecurityFilterChain名詞不能修改
查看 initDelegate方法。
3、查看 FilterChainProxy類
FilterChainProxy類
本質(zhì)也是一個(gè) Filter,所以查看 doFilter方法。留意該類里面的屬性。
public class FilterChainProxy extends GenericFilterBean { private static final Log logger = LogFactory.getLog(FilterChainProxy.class); private static final String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED"); // 過(guò)濾器鏈 private List<SecurityFilterChain> filterChains; private FilterChainProxy.FilterChainValidator filterChainValidator; private HttpFirewall firewall;
3.1 查看 doFilterInternal方法。
驚不驚喜?15個(gè)過(guò)濾器都在這里了!
3.2 查看 getFilters方法。
原來(lái)這些過(guò)濾器都被封裝進(jìn) SecurityFilterChain對(duì)象中。
4 查看 SecurityFilterChain接口
SecurityFilterChain類
是個(gè)接口,實(shí)現(xiàn)類也只有一個(gè) DefaultSecurityFilterChain類
。
DefaultSecurityFilterChain類的構(gòu)造方法,初始化了 List filters,是通過(guò)傳參放進(jìn)去的。
過(guò)濾器鏈參數(shù)是什么時(shí)候傳入的?
5 查看 SpringBootWebSecurityConfiguration類
創(chuàng)建 Spring Security 過(guò)濾器鏈?zhǔn)墙唤o Spring boot 自動(dòng)配置,由 SpringBootWebSecurityConfiguration類
創(chuàng)建注入。
查看 WebSecurityConfigurerAdapter類。
然后會(huì)注入 HttpSecurity對(duì)象,HttpSecurity可以理解為 Spring Security 的 http核心配置,存放 Spring Security 中的過(guò)濾器鏈、請(qǐng)求匹配路徑等相關(guān)認(rèn)證授權(quán)的重要方法。
然后開始創(chuàng)建 Spring Security 過(guò)濾器鏈了,是交給 Spring Boot自動(dòng)配置,一共有 15個(gè)過(guò)濾器。
使用 OrderedFilter進(jìn)行代理,并設(shè)置了order屬性。
添加完成后,將這些過(guò)濾器再封裝為 DefaultSecurityFilterChain對(duì)象。
最后通過(guò) WebSecurityConfiguration配置加載 springSecurityFilterChain,WebSecurityConfiguration中維護(hù)了securityFilterChains屬性,會(huì)存放過(guò)濾器鏈中所有的過(guò)濾器。
總結(jié):
Spring boot 通過(guò) DelegatingFilterProxyRegistrationBean注冊(cè)過(guò)濾器,名字為 springSecurityFilterChain,并生成 DelegatingFilterProxy代理對(duì)象并注冊(cè)到 IoC中。最終真正調(diào)用 FilterChainProxy過(guò)濾器的 doFilter 獲取到 Spring Security 過(guò)濾器鏈。
Spring Security的過(guò)濾器鏈在底層是封裝在 SecurityFilterChain接口中的。
二、過(guò)濾器鏈的執(zhí)行流程
1、調(diào)用 OncePerRequestFilter過(guò)濾器
首先進(jìn)入的是 OncePerRequestFilter 過(guò)濾器。
OncePerRequestFilter是為了確保一次請(qǐng)求中只通過(guò)一次filter,而不需要重復(fù)的執(zhí)行。
會(huì)進(jìn)入 DelegatingFilterProxy代理對(duì)象中 invokeDelegate方法,實(shí)際真正執(zhí)行的是 FilterChainProxy過(guò)濾器的 doFilter 方法。
2、查看 FilterChainProxy過(guò)濾器
查看 FilterChainProxy過(guò)濾器中的 doFilterInternal方法。
然后首先進(jìn)入FilterChainProxy中的 doFilterInternal方法。
doFilterInternal方法中會(huì)調(diào)用 getFilters方法,會(huì)從過(guò)濾器鏈中拿出所有的攔截器。
然后創(chuàng)建一個(gè) VirtualFilterChain對(duì)象,一個(gè)虛擬的過(guò)濾器鏈,并執(zhí)行其中的 doFilter 方法。使用過(guò)濾器對(duì)當(dāng)前請(qǐng)求進(jìn)行層層過(guò)濾。
3、查看 FilterSecurityInterceptor過(guò)濾器
最后進(jìn)入到 FilterSecurityInterceptor過(guò)濾器中,該過(guò)濾器是過(guò)濾器鏈的最后一個(gè)過(guò)濾器,invoke方法中。
先調(diào)用父類的 beforeInvocation方法,之后調(diào)用 filterChain的 doFilter方法,之后調(diào)用父類的 finallyInvocation和afterInvocation方法。
在 beforeInvocation方法中,如果當(dāng)前的請(qǐng)求沒有通過(guò)認(rèn)證,會(huì)拋出 Access is denied異常,這個(gè)異常會(huì)被ExceptionTranslationFilter過(guò)濾器處理。如果拋出的異常是 AuthenticationException,則執(zhí)行方法sendStartAuthentication方法。
最終調(diào)用 EntryPoint的 commence方法,發(fā)布異常。
三、Spring Security常見過(guò)濾器
1、WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
其主要用于集成 SecurityContext到 Spring異步執(zhí)行機(jī)制中的 WebAsyncManager。
2、SecurityContextPersistenceFilter
org.springframework.security.web.context.SecurityContextPersistenceFilter
其主要是使用 SecurityContextRepository在 session中保存或更新一個(gè)SecurityContext,并將 SecurityContext給以后的過(guò)濾器使用,來(lái)為后續(xù) filter建立所需的上下文。SecurityContext
中存儲(chǔ)了當(dāng)前用戶的認(rèn)證以及權(quán)限信息。
3、HeaderWriterFilter
org.springframework.security.web.header.HeaderWriterFilter
其主要是向請(qǐng)求的 Header中添加相應(yīng)的信息,可在 http標(biāo)簽內(nèi)部使用 security:headers來(lái)控制。
4、CsrfFilter
org.springframework.security.web.csrf.CsrfFilter
csrf又稱跨域請(qǐng)求偽造
,SpringSecurity會(huì)對(duì)所有 post請(qǐng)求驗(yàn)證是否包含系統(tǒng)生成的 csrf的 token信息,
如果不包含,則報(bào)錯(cuò)。起到防止csrf攻擊的效果。
5、LogoutFilter
org.springframework.security.web.authentication.logout.LogoutFilter
其主要用于實(shí)現(xiàn)用戶退出,清除認(rèn)證信息。默認(rèn)匹配 URL為 /logout的請(qǐng)求。
6、UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
其主要用于認(rèn)證操作,默認(rèn)匹配URL為 /login且必須為POST請(qǐng)求。
7、DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
如果沒有在配置文件中指定認(rèn)證頁(yè)面,則由該過(guò)濾器生成一個(gè)默認(rèn)認(rèn)證頁(yè)面。
8、DefaultLogoutPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
由此過(guò)濾器可以生產(chǎn)一個(gè)默認(rèn)的退出登錄頁(yè)面
9、BasicAuthenticationFilter
org.springframework.security.web.authentication.www.BasicAuthenticationFilter
此過(guò)濾器會(huì)自動(dòng)解析 HTTP請(qǐng)求中頭部名字為 Authentication,且以 Basic開頭的頭信息。
10、RequestCacheAwareFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
通過(guò)HttpSessionRequestCache內(nèi)部維護(hù)了一個(gè) RequestCache,用于緩存 HttpServletRequest。
11、SecurityContextHolderAwareRequestFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
針對(duì) ServletRequest進(jìn)行了一次包裝,使得 request具有更加豐富的 API。
12、AnonymousAuthenticationFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
當(dāng) SecurityContextHolder中認(rèn)證信息為空,則會(huì)創(chuàng)建一個(gè)匿名用戶
存入到 SecurityContextHolder中。
Spring Security為了兼容未登錄的訪問(wèn),也走了一套認(rèn)證流程,只不過(guò)是一個(gè)匿名的身份。
13、SessionManagementFilter
org.springframework.security.web.session.SessionManagementFilter
其主要用于限制同一用戶開啟多個(gè)會(huì)話的數(shù)量。
14、ExceptionTranslationFilter
org.springframework.security.web.access.ExceptionTranslationFilter
異常轉(zhuǎn)換過(guò)濾器位于整個(gè) springSecurityFilterChain的后方,用來(lái)轉(zhuǎn)換整個(gè)鏈路中出現(xiàn)的異常。ExceptionTranslationFilter過(guò)濾器會(huì)攔截處理 AccessDeniedException和 AuthenticationException并添加到HTTP響應(yīng)中。
15、FilterSecurityInterceptor
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
獲取所配置資源訪問(wèn)的授權(quán)信息,根據(jù) SecurityContextHolder中存儲(chǔ)的用戶信息來(lái)決定其是否有權(quán)限。
到此這篇關(guān)于Spring Security過(guò)濾器鏈加載執(zhí)行流程源碼分析的文章就介紹到這了,更多相關(guān)Spring Security過(guò)濾器鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring cloud gateway 全局過(guò)濾器的實(shí)現(xiàn)
全局過(guò)濾器作用于所有的路由,不需要單獨(dú)配置,我們可以用它來(lái)實(shí)現(xiàn)很多統(tǒng)一化處理的業(yè)務(wù)需求,這篇文章主要介紹了spring cloud gateway 全局過(guò)濾器的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03關(guān)于Java創(chuàng)建線程的2種方式以及對(duì)比
這篇文章主要給大家介紹了關(guān)于Java創(chuàng)建線程的2種方式以及對(duì)比的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01SpringBoot3.0集成MybatisPlus的實(shí)現(xiàn)方法
本文主要介紹了SpringBoot3.0集成MybatisPlus的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08Java concurrency之CountDownLatch原理和示例_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
CountDownLatch是一個(gè)同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待。 下面通過(guò)本文給大家分享Java concurrency之CountDownLatch原理和示例,需要的的朋友參考下吧2017-06-06springboot項(xiàng)目接入第三方qq郵箱驗(yàn)證登錄的全過(guò)程
互聯(lián)網(wǎng)發(fā)展到現(xiàn)在,相必大家都知道發(fā)送郵件應(yīng)該是網(wǎng)站的必備功能之一,下面這篇文章主要給大家介紹了關(guān)于springboot項(xiàng)目接入第三方qq郵箱驗(yàn)證登錄的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04一文帶你掌握J(rèn)ava8強(qiáng)大的StreamAPI
Java8API添加了一個(gè)新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數(shù)據(jù)。Stream 使用一種類似用SQL 語(yǔ)句從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)的直觀方式來(lái)提供一種對(duì) Java 集合運(yùn)算和表達(dá)的高階抽象。Stream API可以極大提高Java程序員的生產(chǎn)力,讓程序員寫出高效率、干凈、簡(jiǎn)潔的代碼2021-10-10