全面解析Spring Security 內(nèi)置 Filter
1. 前言
上一文我們使用 Spring Security 實現(xiàn)了各種登錄聚合的場面。其中我們是通過在 UsernamePasswordAuthenticationFilter
之前一個自定義的過濾器實現(xiàn)的。我怎么知道自定義過濾器要加在 UsernamePasswordAuthenticationFilter
之前。我在這個系列開篇說了Spring Security 權(quán)限控制的一個核心關(guān)鍵就是 過濾器鏈 ,這些過濾器如下圖進(jìn)行過濾傳遞,甚至比這個更復(fù)雜!這只是一個最小單元。
Spring Security 內(nèi)置了一些過濾器,他們各有各的本事。如果你掌握了這些過濾器,很多實際開發(fā)中的需求和問題都很容易解決。今天我們來見識一下這些內(nèi)置的過濾器。
2. 內(nèi)置過濾器初始化
在 Spring Security 初始化核心過濾器時 HttpSecurity
會通過將 Spring Security 內(nèi)置的一些過濾器以 FilterComparator
提供的規(guī)則進(jìn)行比較按照比較結(jié)果進(jìn)行排序注冊。
2.1 排序規(guī)則
FilterComparator
維護(hù)了一個順序的注冊表 filterToOrder
。
FilterComparator() { Step order = new Step(INITIAL_ORDER, ORDER_STEP); put(ChannelProcessingFilter.class, order.next()); put(ConcurrentSessionFilter.class, order.next()); put(WebAsyncManagerIntegrationFilter.class, order.next()); put(SecurityContextPersistenceFilter.class, order.next()); put(HeaderWriterFilter.class, order.next()); put(CorsFilter.class, order.next()); put(CsrfFilter.class, order.next()); put(LogoutFilter.class, order.next()); filterToOrder.put( "org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter", order.next()); filterToOrder.put( "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter", order.next()); put(X509AuthenticationFilter.class, order.next()); put(AbstractPreAuthenticatedProcessingFilter.class, order.next()); filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next()); filterToOrder.put( "org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter", order.next()); filterToOrder.put( "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter", order.next()); put(UsernamePasswordAuthenticationFilter.class, order.next()); put(ConcurrentSessionFilter.class, order.next()); filterToOrder.put( "org.springframework.security.openid.OpenIDAuthenticationFilter", order.next()); put(DefaultLoginPageGeneratingFilter.class, order.next()); put(DefaultLogoutPageGeneratingFilter.class, order.next()); put(ConcurrentSessionFilter.class, order.next()); put(DigestAuthenticationFilter.class, order.next()); filterToOrder.put( "org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter", order.next()); put(BasicAuthenticationFilter.class, order.next()); put(RequestCacheAwareFilter.class, order.next()); put(SecurityContextHolderAwareRequestFilter.class, order.next()); put(JaasApiIntegrationFilter.class, order.next()); put(RememberMeAuthenticationFilter.class, order.next()); put(AnonymousAuthenticationFilter.class, order.next()); filterToOrder.put( "org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter", order.next()); put(SessionManagementFilter.class, order.next()); put(ExceptionTranslationFilter.class, order.next()); put(FilterSecurityInterceptor.class, order.next()); put(SwitchUserFilter.class, order.next()); }
這些就是所有內(nèi)置的過濾器。 他們是通過下面的方法獲取自己的序號:
private Integer getOrder(Class<?> clazz) { while (clazz != null) { Integer result = filterToOrder.get(clazz.getName()); if (result != null) { return result; } clazz = clazz.getSuperclass(); } return null; }
通過過濾器的類全限定名從注冊表 filterToOrder
中獲取自己的序號,如果沒有直接獲取到序號通過遞歸獲取父類在注冊表中的序號作為自己的序號,序號越小優(yōu)先級越高。上面的過濾器并非全部會被初始化。有的需要額外引入一些功能包,有的看 HttpSecurity
的配置情況。 在上一篇文章中。我們禁用了 CSRF
功能,就意味著 CsrfFilter
不會被注冊。
3. 內(nèi)置過濾器講解
接下來我們就對這些內(nèi)置過濾器進(jìn)行一個系統(tǒng)的認(rèn)識。我們將按照默認(rèn)順序進(jìn)行講解。
3.1 ChannelProcessingFilter
ChannelProcessingFilter
通常是用來過濾哪些請求必須用 https
協(xié)議, 哪些請求必須用 http
協(xié)議, 哪些請求隨便用哪個協(xié)議都行。它主要有兩個屬性:
ChannelDecisionManager
用來判斷請求是否符合既定的協(xié)議規(guī)則。它維護(hù)了一個ChannelProcessor
列表 這些ChannelProcessor
是具體用來執(zhí)行ANY_CHANNEL
策略 (任何通道都可以),REQUIRES_SECURE_CHANNEL
策略 (只能通過https
通道),REQUIRES_INSECURE_CHANNEL
策略 (只能通過http
通道)。FilterInvocationSecurityMetadataSource
用來存儲 url 與 對應(yīng)的ANY_CHANNEL
、REQUIRES_SECURE_CHANNEL
、REQUIRES_INSECURE_CHANNEL
的映射關(guān)系。
ChannelProcessingFilter
通過 HttpScurity#requiresChannel()
等相關(guān)方法引入其配置對象 ChannelSecurityConfigurer
來進(jìn)行配置。
3.2 ConcurrentSessionFilter
ConcurrentSessionFilter
主要用來判斷session
是否過期以及更新最新的訪問時間。其流程為:
session
檢測,如果不存在直接放行去執(zhí)行下一個過濾器。存在則進(jìn)行下一步。根據(jù)sessionid
從SessionRegistry
中獲取SessionInformation
,從SessionInformation
中獲取session
是否過期;沒有過期則更新SessionInformation
中的訪問日期;
如果過期,則執(zhí)行doLogout()
方法,這個方法會將session
無效,并將 SecurityContext
中的Authentication
中的權(quán)限置空,同時在SecurityContenxtHoloder
中清除SecurityContext
然后查看是否有跳轉(zhuǎn)的 expiredUrl
,如果有就跳轉(zhuǎn),沒有就輸出提示信息。
ConcurrentSessionFilter
通過SessionManagementConfigurer
來進(jìn)行配置。
3.3 WebAsyncManagerIntegrationFilter
WebAsyncManagerIntegrationFilter
用于集成SecurityContext到Spring異步執(zhí)行機(jī)制中的WebAsyncManager。用來處理異步請求的安全上下文。具體邏輯為:
從請求屬性上獲取所綁定的WebAsyncManager
,如果尚未綁定,先做綁定。從asyncManager
中獲取 key
為 CALLABLE_INTERCEPTOR_KEY
的安全上下文多線程處理器 SecurityContextCallableProcessingInterceptor
, 如果獲取到的為 null
,
新建一個 SecurityContextCallableProcessingInterceptor
并綁定 CALLABLE_INTERCEPTOR_KEY
注冊到 asyncManager
中。
這里簡單說一下 SecurityContextCallableProcessingInterceptor
。它實現(xiàn)了接口 CallableProcessingInterceptor
,
當(dāng)它被應(yīng)用于一次異步執(zhí)行時,beforeConcurrentHandling()
方法會在調(diào)用者線程執(zhí)行,該方法會相應(yīng)地從當(dāng)前線程獲取SecurityContext
,然后被調(diào)用者線程中執(zhí)行邏輯時,會使用這個 SecurityContext
,從而實現(xiàn)安全上下文從調(diào)用者線程到被調(diào)用者線程的傳輸。
WebAsyncManagerIntegrationFilter
通過 WebSecurityConfigurerAdapter#getHttp()
方法添加到 HttpSecurity
中成為 DefaultSecurityFilterChain
的一個鏈節(jié)。
3.4 SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
主要控制 SecurityContext
的在一次請求中的生命周期 。 請求來臨時,創(chuàng)建SecurityContext
安全上下文信息,請求結(jié)束時清空 SecurityContextHolder
。
SecurityContextPersistenceFilter
通過 HttpScurity#securityContext()
及相關(guān)方法引入其配置對象 SecurityContextConfigurer
來進(jìn)行配置。
3.5 HeaderWriterFilter
HeaderWriterFilter
用來給 http
響應(yīng)添加一些 Header
,比如 X-Frame-Options
, X-XSS-Protection
,X-Content-Type-Options
。
你可以通過 HttpScurity#headers()
來定制請求Header
。
3.6 CorsFilter
跨域相關(guān)的過濾器。這是Spring MVC Java
配置和XML
命名空間 CORS
配置的替代方法, 僅對依賴于spring-web
的應(yīng)用程序有用(不適用于spring-webmvc
)或 要求在javax.servlet.Filter
級別進(jìn)行CORS檢查的安全約束鏈接。這個是目前官方的一些解讀,但是我還是不太清楚實際機(jī)制。
你可以通過 HttpSecurity#cors()
來定制。
3.7 CsrfFilter
CsrfFilter
用于防止csrf
攻擊,前后端使用json交互需要注意的一個問題。
你可以通過 HttpSecurity.csrf()
來開啟或者關(guān)閉它。在你使用 jwt
等 token
技術(shù)時,是不需要這個的。
3.8 LogoutFilter
LogoutFilter
很明顯這是處理注銷的過濾器。
你可以通過 HttpSecurity.logout()
來定制注銷邏輯,非常有用。
3.9 OAuth2AuthorizationRequestRedirectFilter
和上面的有所不同,這個需要依賴 spring-scurity-oauth2
相關(guān)的模塊。該過濾器是處理 OAuth2
請求首選重定向相關(guān)邏輯的。
3.10 Saml2WebSsoAuthenticationRequestFilter
這個需要用到 Spring Security SAML
模塊,這是一個基于 SMAL
的 SSO
單點登錄請求認(rèn)證過濾器。
關(guān)于SAML
SAML
即安全斷言標(biāo)記語言,英文全稱是 Security Assertion Markup Language
。它是一個基于 XML
的標(biāo)準(zhǔn),用于在不同的安全域(security domain
)之間交換認(rèn)證和授權(quán)數(shù)據(jù)。在 SAML
標(biāo)準(zhǔn)定義了身份提供者 (identity provider
) 和服務(wù)提供者 (service provider
),這兩者構(gòu)成了前面所說的不同的安全域。 SAML
是 OASIS
組織安全服務(wù)技術(shù)委員會(Security Services Technical Committee) 的產(chǎn)品。
SAML
(Security Assertion Markup Language)是一個 XML
框架,也就是一組協(xié)議,可以用來傳輸安全聲明。比如,兩臺遠(yuǎn)程機(jī)器之間要通訊,為了保證安全,我們可以采用加密等措施,也可以采用 SAML
來傳輸,傳輸?shù)臄?shù)據(jù)以 XML
形式,符合 SAML
規(guī)范,這樣我們就可以不要求兩臺機(jī)器采用什么樣的系統(tǒng),只要求能理解 SAML
規(guī)范即可,顯然比傳統(tǒng)的方式更好。SAML
規(guī)范是一組 Schema
定義。
可以這么說,在Web Service
領(lǐng)域,schema
就是規(guī)范,在 Java
領(lǐng)域,API
就是規(guī)范
3.11 X509AuthenticationFilter
X509
認(rèn)證過濾器。你可以通過 HttpSecurity#X509()
來啟用和配置相關(guān)功能。
3.12 AbstractPreAuthenticatedProcessingFilter
AbstractPreAuthenticatedProcessingFilter
處理處理經(jīng)過預(yù)先認(rèn)證的身份驗證請求的過濾器的基類,其中認(rèn)證主體已經(jīng)由外部系統(tǒng)進(jìn)行了身份驗證。 目的只是從傳入請求中提取主體上的必要信息,而不是對它們進(jìn)行身份驗證。
你可以繼承該類進(jìn)行具體實現(xiàn)并通過 HttpSecurity#addFilter
方法來添加個性化的AbstractPreAuthenticatedProcessingFilter
。
3.13 CasAuthenticationFilter
CAS
單點登錄認(rèn)證過濾器 。依賴 Spring Security CAS 模塊
3.14 OAuth2LoginAuthenticationFilter
這個需要依賴 spring-scurity-oauth2
相關(guān)的模塊。OAuth2
登錄認(rèn)證過濾器。處理通過 OAuth2
進(jìn)行認(rèn)證登錄的邏輯。
3.15 Saml2WebSsoAuthenticationFilter
這個需要用到 Spring Security SAML
模塊,這是一個基于 SMAL
的 SSO
單點登錄認(rèn)證過濾器。 關(guān)于SAML
3.16 UsernamePasswordAuthenticationFilter
這個看過我相關(guān)文章的應(yīng)該不陌生了。處理用戶以及密碼認(rèn)證的核心過濾器。認(rèn)證請求提交的username
和 password
,被封裝成token
進(jìn)行一系列的認(rèn)證,便是主要通過這個過濾器完成的,在表單認(rèn)證的方法中,這是最最關(guān)鍵的過濾器。
你可以通過 HttpSecurity#formLogin()
及相關(guān)方法引入其配置對象 FormLoginConfigurer
來進(jìn)行配置。 我們在 Spring Security 實戰(zhàn)干貨: 玩轉(zhuǎn)自定義登錄 已經(jīng)對其進(jìn)行過個性化的配置和魔改。
3.17 ConcurrentSessionFilter
參見 3.2 ConcurrentSessionFilter 。 該過濾器可能會被多次執(zhí)行。
3.18 OpenIDAuthenticationFilter
基于OpenID
認(rèn)證協(xié)議的認(rèn)證過濾器。 你需要在依賴中依賴額外的相關(guān)模塊才能啟用它。
3.19 DefaultLoginPageGeneratingFilter
生成默認(rèn)的登錄頁。默認(rèn) /login
。
3.20 DefaultLogoutPageGeneratingFilter
生成默認(rèn)的退出頁。 默認(rèn) /logout
。
3.21 ConcurrentSessionFilter
參見 3.2 ConcurrentSessionFilter 。 該過濾器可能會被多次執(zhí)行。
3.23 DigestAuthenticationFilter
Digest
身份驗證是 Web
應(yīng)用程序中流行的可選的身份驗證機(jī)制 。DigestAuthenticationFilter
能夠處理 HTTP
頭中顯示的摘要式身份驗證憑據(jù)。你可以通過 HttpSecurity#addFilter()
來啟用和配置相關(guān)功能。
3.24 BasicAuthenticationFilter
和Digest
身份驗證一樣都是Web
應(yīng)用程序中流行的可選的身份驗證機(jī)制 。 BasicAuthenticationFilter
負(fù)責(zé)處理 HTTP
頭中顯示的基本身份驗證憑據(jù)。這個 Spring Security 的 Spring Boot 自動配置默認(rèn)是啟用的 。
BasicAuthenticationFilter
通過 HttpSecurity#httpBasic()
及相關(guān)方法引入其配置對象 HttpBasicConfigurer
來進(jìn)行配置。
3.25 RequestCacheAwareFilter
用于用戶認(rèn)證成功后,重新恢復(fù)因為登錄被打斷的請求。當(dāng)匿名訪問一個需要授權(quán)的資源時。會跳轉(zhuǎn)到認(rèn)證處理邏輯,此時請求被緩存。在認(rèn)證邏輯處理完畢后,從緩存中獲取最開始的資源請求進(jìn)行再次請求。
RequestCacheAwareFilter
通過 HttpScurity#requestCache()
及相關(guān)方法引入其配置對象 RequestCacheConfigurer
來進(jìn)行配置。
3.26 SecurityContextHolderAwareRequestFilter
用來 實現(xiàn)j2ee
中 Servlet Api
一些接口方法, 比如 getRemoteUser
方法、isUserInRole
方法,在使用 Spring Security 時其實就是通過這個過濾器來實現(xiàn)的。
SecurityContextHolderAwareRequestFilter
通過 HttpSecurity.servletApi()
及相關(guān)方法引入其配置對象 ServletApiConfigurer
來進(jìn)行配置。
3.27 JaasApiIntegrationFilter
適用于JAAS
(Java
認(rèn)證授權(quán)服務(wù))。 如果 SecurityContextHolder
中擁有的 Authentication
是一個 JaasAuthenticationToken
,那么該 JaasApiIntegrationFilter
將使用包含在 JaasAuthenticationToken
中的 Subject
繼續(xù)執(zhí)行 FilterChain
。
3.28 RememberMeAuthenticationFilter
處理 記住我
功能的過濾器。
RememberMeAuthenticationFilter
通過 HttpSecurity.rememberMe()
及相關(guān)方法引入其配置對象 RememberMeConfigurer
來進(jìn)行配置。
3.29 AnonymousAuthenticationFilter
匿名認(rèn)證過濾器。對于 Spring Security
來說,所有對資源的訪問都是有 Authentication
的。對于無需登錄(UsernamePasswordAuthenticationFilter
)直接可以訪問的資源,會授予其匿名用戶身份。
AnonymousAuthenticationFilter
通過 HttpSecurity.anonymous()
及相關(guān)方法引入其配置對象 AnonymousConfigurer
來進(jìn)行配置。
3.30 SessionManagementFilter
Session
管理器過濾器,內(nèi)部維護(hù)了一個 SessionAuthenticationStrategy
用于管理 Session
。
SessionManagementFilter
通過 HttpScurity#sessionManagement()
及相關(guān)方法引入其配置對象 SessionManagementConfigurer
來進(jìn)行配置。
3.31 ExceptionTranslationFilter
主要來傳輸異常事件,還記得之前我們見過的 DefaultAuthenticationEventPublisher
嗎?
3.32 FilterSecurityInterceptor
這個過濾器決定了訪問特定路徑應(yīng)該具備的權(quán)限,訪問的用戶的角色,權(quán)限是什么?訪問的路徑需要什么樣的角色和權(quán)限?這些判斷和處理都是由該類進(jìn)行的。如果你要實現(xiàn)動態(tài)權(quán)限控制就必須研究該類 。
3.33 SwitchUserFilter
SwitchUserFilter
是用來做賬戶切換的。默認(rèn)的切換賬號的url
為/login/impersonate
,默認(rèn)注銷切換賬號的url
為/logout/impersonate
,默認(rèn)的賬號參數(shù)為username
。
你可以通過此類實現(xiàn)自定義的賬戶切換。
4. 總結(jié)
所有內(nèi)置的 31個過濾器作用都講解完了,有一些默認(rèn)已經(jīng)啟用。有一些需要引入特定的包并且對 HttpSecurity
進(jìn)行配置才會生效 。而且它們的順序是既定的。 只有你了解這些過濾器你才能基于業(yè)務(wù)深度定制 Spring Security 。
到此這篇關(guān)于全面解析Spring Security 內(nèi)置 Filter的文章就介紹到這了,更多相關(guān)Spring Security 內(nèi)置 Filter內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java執(zhí)行SQL語句實現(xiàn)查詢的通用方法詳解
這篇文章主要介紹了java執(zhí)行SQL語句實現(xiàn)查詢的通用方法詳解,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12面向?qū)ο蠛兔嫦蜻^程的區(qū)別(動力節(jié)點java學(xué)院整理)
很多朋友不清楚面向?qū)ο蠛兔嫦蜻^程有什么區(qū)別,接下來小編給大家整理了關(guān)于面向?qū)ο蠛兔嫦蜻^程的區(qū)別講解,感興趣的朋友可以參考下2017-04-04Java使用FilenameFilter查找出目錄下指定后綴的文件示例
這篇文章主要介紹了Java使用FilenameFilter查找出目錄下指定后綴的文件,結(jié)合實例形式分析了java基于FilenameFilter類的文件遍歷、查找相關(guān)操作技巧,需要的朋友可以參考下2019-10-10Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理方法
這篇文章主要介紹了Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-10-10