全面解析Spring Security 內建 Filter
1. 前言
上一文我們使用 Spring Security 實現了各種登入聚合的場面。其中我們是通過在 UsernamePasswordAuthenticationFilter
之前一個自定義的過濾器實現的。我怎麼知道自定義過濾器要加在 UsernamePasswordAuthenticationFilter
之前。我在這個系列開篇說了 Spring Security 許可權控制的一個核心關鍵就是 過濾器鏈 ,這些過濾器如下圖進行過濾傳遞,甚至比這個更復雜!這只是一個最小單元。
Spring Security 內建了一些過濾器,他們各有各的本事。如果你掌握了這些過濾器,很多實際開發中的需求和問題都很容易解決。今天我們來見識一下這些內建的過濾器。
2. 內建過濾器初始化
在 Spring Security 初始化核心過濾器時 HttpSecurity
會通過將 Spring Security 內建的一些過濾器以 FilterComparator
提供的規則進行比較按照比較結果進行排序註冊。
2.1 排序規則
FilterComparator
維護了一個順序的登錄檔 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()); filterToOrder.put( "org.springframework.security.openid.OpenIDAuthenticationFilter",order.next()); put(DefaultLoginPageGeneratingFilter.class,order.next()); put(DefaultLogoutPageGeneratingFilter.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()); }
這些就是所有內建的過濾器。 他們是通過下面的方法獲取自己的序號:
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
HttpSecurity
的配置情況。 在上一篇文章中。我們禁用了 CSRF
功能,就意味著 CsrfFilter
不會被註冊。
3. 內建過濾器講解
接下來我們就對這些內建過濾器進行一個系統的認識。我們將按照預設順序進行講解。
3.1 ChannelProcessingFilter
ChannelProcessingFilter
通常是用來過濾哪些請求必須用 https
協議, 哪些請求必須用 http
協議, 哪些請求隨便用哪個協議都行。它主要有兩個屬性:
ChannelDecisionManager
用來判斷請求是否符合既定的協議規則。它維護了一個ChannelProcessor
列表 這些ChannelProcessor
是具體用來執行ANY_CHANNEL
策略 (任何通道都可以),REQUIRES_SECURE_CHANNEL
策略 (只能通過https
通道),REQUIRES_INSECURE_CHANNEL
策略 (只能通過http
通道)。FilterInvocationSecurityMetadataSource
用來儲存 url 與 對應的ANY_CHANNEL
、REQUIRES_SECURE_CHANNEL
、REQUIRES_INSECURE_CHANNEL
的對映關係。
ChannelProcessingFilter
通過 HttpScurity#requiresChannel()
等相關方法引入其配置物件 ChannelSecurityConfigurer
來進行配置。
3.2 ConcurrentSessionFilter
ConcurrentSessionFilter
主要用來判斷session
是否過期以及更新最新的訪問時間。其流程為:
session
檢測,如果不存在直接放行去執行下一個過濾器。存在則進行下一步。根據sessionid
從SessionRegistry
中獲取SessionInformation
,從SessionInformation
中獲取session
是否過期;沒有過期則更新SessionInformation
中的訪問日期;
如果過期,則執行doLogout()
方法,這個方法會將session
無效,並將 SecurityContext
中的Authentication
中的許可權置空,同時在SecurityContenxtHoloder
中清除SecurityContext
然後檢視是否有跳轉的 expiredUrl
,如果有就跳轉,沒有就輸出提示資訊。
ConcurrentSessionFilter
通過SessionManagementConfigurer
來進行配置。
3.3 WebAsyncManagerIntegrationFilter
WebAsyncManagerIntegrationFilter
用於整合SecurityContext到Spring非同步執行機制中的WebAsyncManager。用來處理非同步請求的安全上下文。具體邏輯為:
從請求屬性上獲取所繫結的WebAsyncManager
,如果尚未繫結,先做繫結。從asyncManager
中獲取 key
為 CALLABLE_INTERCEPTOR_KEY
的安全上下文多執行緒處理器 SecurityContextCallableProcessingInterceptor
,如果獲取到的為 null
,
新建一個 SecurityContextCallableProcessingInterceptor
並繫結 CALLABLE_INTERCEPTOR_KEY
註冊到 asyncManager
中。
這裡簡單說一下 SecurityContextCallableProcessingInterceptor
。它實現了介面 CallableProcessingInterceptor
,
當它被應用於一次非同步執行時,beforeConcurrentHandling()
方法會在呼叫者執行緒執行,該方法會相應地從當前執行緒獲取SecurityContext
,然後被呼叫者執行緒中執行邏輯時,會使用這個 SecurityContext
,從而實現安全上下文從呼叫者執行緒到被呼叫者執行緒的傳輸。
WebAsyncManagerIntegrationFilter
通過 WebSecurityConfigurerAdapter#getHttp()
方法新增到 HttpSecurity
中成為 DefaultSecurityFilterChain
的一個鏈節。
3.4 SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
主要控制 SecurityContext
的在一次請求中的生命週期 。 請求來臨時,建立SecurityContext
安全上下文資訊,請求結束時清空 SecurityContextHolder
。
SecurityContextPersistenceFilter
通過 HttpScurity#securityContext()
及相關方法引入其配置物件 SecurityContextConfigurer
來進行配置。
3.5 HeaderWriterFilter
HeaderWriterFilter
用來給 http
響應新增一些 Header
,比如 X-Frame-Options
,X-XSS-Protection
,X-Content-Type-Options
。
你可以通過 HttpScurity#headers()
來定製請求Header
。
3.6 CorsFilter
跨域相關的過濾器。這是Spring MVC Java
配置和XML
名稱空間 CORS
配置的替代方法, 僅對依賴於spring-web
的應用程式有用(不適用於spring-webmvc
)或 要求在javax.servlet.Filter
級別進行CORS檢查的安全約束連結。這個是目前官方的一些解讀,但是我還是不太清楚實際機制。
你可以通過 HttpSecurity#cors()
來定製。
3.7 CsrfFilter
CsrfFilter
用於防止csrf
攻擊,前後端使用json互動需要注意的一個問題。
你可以通過 HttpSecurity.csrf()
來開啟或者關閉它。在你使用 jwt
等 token
技術時,是不需要這個的。
3.8 LogoutFilter
LogoutFilter
很明顯這是處理登出的過濾器。
你可以通過 HttpSecurity.logout()
來定製登出邏輯,非常有用。
3.9 OAuth2AuthorizationRequestRedirectFilter
和上面的有所不同,這個需要依賴 spring-scurity-oauth2
相關的模組。該過濾器是處理 OAuth2
請求首選重定向相關邏輯的。以後會我會帶你們認識它,請多多關注公眾號:Felordcn
。
3.10 Saml2WebSsoAuthenticationRequestFilter
這個需要用到 Spring Security SAML
模組,這是一個基於 SMAL
的 SSO
單點登入請求認證過濾器。
關於SAML
SAML
即安全斷言標記語言,英文全稱是 Security Assertion Markup Language
。它是一個基於 XML
的標準,用於在不同的安全域(security domain
)之間交換認證和授權資料。在 SAML
標準定義了身份提供者 (identity provider
) 和服務提供者 (service provider
),這兩者構成了前面所說的不同的安全域。 SAML
是 OASIS
組織安全服務技術委員會(Security Services Technical Committee) 的產品。
SAML
(Security Assertion Markup Language)是一個 XML
框架,也就是一組協議,可以用來傳輸安全宣告。比如,兩臺遠端機器之間要通訊,為了保證安全,我們可以採用加密等措施,也可以採用 SAML
來傳輸,傳輸的資料以 XML
形式,符合 SAML
規範,這樣我們就可以不要求兩臺機器採用什麼樣的系統,只要求能理解 SAML
規範即可,顯然比傳統的方式更好。SAML
規範是一組 Schema
定義。
可以這麼說,在Web Service
領域,schema
就是規範,在 Java
領域,API
就是規範
3.11 X509AuthenticationFilter
X509
認證過濾器。你可以通過 HttpSecurity#X509()
來啟用和配置相關功能。
3.12 AbstractPreAuthenticatedProcessingFilter
AbstractPreAuthenticatedProcessingFilter
處理處理經過預先認證的身份驗證請求的過濾器的基類,其中認證主體已經由外部系統進行了身份驗證。 目的只是從傳入請求中提取主體上的必要資訊,而不是對它們進行身份驗證。
你可以繼承該類進行具體實現並通過 HttpSecurity#addFilter
方法來添加個性化的AbstractPreAuthenticatedProcessingFilter
。
3.13 CasAuthenticationFilter
CAS
單點登入認證過濾器 。依賴 Spring Security CAS 模組
3.14 OAuth2LoginAuthenticationFilter
這個需要依賴 spring-scurity-oauth2
相關的模組。OAuth2
登入認證過濾器。處理通過 OAuth2
進行認證登入的邏輯。
3.15 Saml2WebSsoAuthenticationFilter
這個需要用到 Spring Security SAML
模組,這是一個基於 SMAL
的 SSO
單點登入認證過濾器。 關於SAML
3.16 UsernamePasswordAuthenticationFilter
這個看過我相關文章的應該不陌生了。處理使用者以及密碼認證的核心過濾器。認證請求提交的username
和 password
,被封裝成token
進行一系列的認證,便是主要通過這個過濾器完成的,在表單認證的方法中,這是最最關鍵的過濾器。
你可以通過 HttpSecurity#formLogin()
及相關方法引入其配置物件 FormLoginConfigurer
來進行配置。 我們在 Spring Security 實戰乾貨: 玩轉自定義登入 已經對其進行過個性化的配置和魔改。
3.17 ConcurrentSessionFilter
參見 3.2 ConcurrentSessionFilter 。 該過濾器可能會被多次執行。
3.18 OpenIDAuthenticationFilter
基於OpenID
認證協議的認證過濾器。 你需要在依賴中依賴額外的相關模組才能啟用它。
3.19 DefaultLoginPageGeneratingFilter
生成預設的登入頁。預設 /login
。
3.20 DefaultLogoutPageGeneratingFilter
生成預設的退出頁。 預設 /logout
。
3.21 ConcurrentSessionFilter
參見 3.2 ConcurrentSessionFilter 。 該過濾器可能會被多次執行。
3.23 DigestAuthenticationFilter
Digest
身份驗證是 Web
應用程式中流行的可選的身份驗證機制 。DigestAuthenticationFilter
能夠處理 HTTP
頭中顯示的摘要式身份驗證憑據。你可以通過 HttpSecurity#addFilter()
來啟用和配置相關功能。
3.24 BasicAuthenticationFilter
和Digest
身份驗證一樣都是Web
應用程式中流行的可選的身份驗證機制 。 BasicAuthenticationFilter
負責處理 HTTP
頭中顯示的基本身份驗證憑據。這個 Spring Security 的 Spring Boot 自動配置預設是啟用的 。
BasicAuthenticationFilter
通過 HttpSecurity#httpBasic()
及相關方法引入其配置物件 HttpBasicConfigurer
來進行配置。
3.25 RequestCacheAwareFilter
用於使用者認證成功後,重新恢復因為登入被打斷的請求。當匿名訪問一個需要授權的資源時。會跳轉到認證處理邏輯,此時請求被快取。在認證邏輯處理完畢後,從快取中獲取最開始的資源請求進行再次請求。
RequestCacheAwareFilter
通過 HttpScurity#requestCache()
及相關方法引入其配置物件 RequestCacheConfigurer
來進行配置。
3.26 SecurityContextHolderAwareRequestFilter
用來 實現j2ee
中 Servlet Api
一些介面方法,比如 getRemoteUser
方法、isUserInRole
方法,在使用 Spring Security 時其實就是通過這個過濾器來實現的。
SecurityContextHolderAwareRequestFilter
通過 HttpSecurity.servletApi()
及相關方法引入其配置物件 ServletApiConfigurer
來進行配置。
3.27 JaasApiIntegrationFilter
適用於JAAS
(Java
認證授權服務)。 如果 SecurityContextHolder
中擁有的 Authentication
是一個 JaasAuthenticationToken
,那麼該 JaasApiIntegrationFilter
將使用包含在 JaasAuthenticationToken
中的 Subject
繼續執行 FilterChain
。
3.28 RememberMeAuthenticationFilter
處理 記住我
功能的過濾器。
RememberMeAuthenticationFilter
通過 HttpSecurity.rememberMe()
及相關方法引入其配置物件 RememberMeConfigurer
來進行配置。
3.29 AnonymousAuthenticationFilter
匿名認證過濾器。對於 Spring Security
來說,所有對資源的訪問都是有 Authentication
的。對於無需登入(UsernamePasswordAuthenticationFilter
)直接可以訪問的資源,會授予其匿名使用者身份。
AnonymousAuthenticationFilter
通過 HttpSecurity.anonymous()
及相關方法引入其配置物件 AnonymousConfigurer
來進行配置。
3.30 SessionManagementFilter
Session
管理器過濾器,內部維護了一個 SessionAuthenticationStrategy
用於管理 Session
。
SessionManagementFilter
通過 HttpScurity#sessionManagement()
及相關方法引入其配置物件 SessionManagementConfigurer
來進行配置。
3.31 ExceptionTranslationFilter
主要來傳輸異常事件,還記得之前我們見過的 DefaultAuthenticationEventPublisher
嗎?
3.32 FilterSecurityInterceptor
這個過濾器決定了訪問特定路徑應該具備的許可權,訪問的使用者的角色,許可權是什麼?訪問的路徑需要什麼樣的角色和許可權?這些判斷和處理都是由該類進行的。如果你要實現動態許可權控制就必須研究該類 。
3.33 SwitchUserFilter
SwitchUserFilter
是用來做賬戶切換的。預設的切換賬號的url
為/login/impersonate
,預設登出切換賬號的url
為/logout/impersonate
,預設的賬號引數為username
。
你可以通過此類實現自定義的賬戶切換。
4. 總結
所有內建的 31個過濾器作用都講解完了,有一些預設已經啟用。有一些需要引入特定的包並且對 HttpSecurity
進行配置才會生效 。而且它們的順序是既定的。 只有你瞭解這些過濾器你才能基於業務深度定製 Spring Security 。
到此這篇關於全面解析Spring Security 內建 Filter的文章就介紹到這了,更多相關Spring Security 內建 Filter內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!