Spring Security 安全過濾器
1. 前言
上一節(jié)中我們了解到了 Spring Security 安全框架是由一系列有序的安全過濾器組成的,本節(jié)將重點討論 Spring Security 內(nèi)置安全過濾器的順序及含義。
Spring 將自己體系內(nèi)的過濾器交由「過濾器代理 FilterChainProxy」管理,Spring Security 在「過濾器代理 FilterChainProxy」中加入了「安全過濾器鏈 SecurityFilterChain」實現(xiàn)安全保護(hù)功能。
在 Spring Security 各個模塊中,內(nèi)置已實現(xiàn)了一系列的「安全過濾器」,可以滿足常見的認(rèn)證、鑒權(quán)等功能需求。
在 Spring Security 5.3.2 中,共內(nèi)置了 33 個安全過濾器。
本節(jié)主要討論 Spring Security 內(nèi)置的安全過濾器。
2. 內(nèi)置安全過濾器
2.1 內(nèi)置安全過濾器的聲明
Spring Security 中的 SecurityFilterChain
對象,是通過 HttpSecurity
類來使用,它還提供了安全過濾器增、減的調(diào)用接口。
如前文所述,安全過濾器的執(zhí)行順序是至關(guān)重要的,而順序的確定是由 FilterComparator
對象實現(xiàn)的。
FilterComparator
類的全路徑為 org.springframework.security.config.annotation.web.builders.FilterComparator
。
在其構(gòu)造的時候配置了過濾器的基本順序,代碼如下:
FilterComparator() {
FilterComparator.Step order = new FilterComparator.Step(100, 100);
this.put(ChannelProcessingFilter.class, order.next());
this.put(ConcurrentSessionFilter.class, order.next());
this.put(WebAsyncManagerIntegrationFilter.class, order.next());
this.put(SecurityContextPersistenceFilter.class, order.next());
this.put(HeaderWriterFilter.class, order.next());
...
this.put(SessionManagementFilter.class, order.next());
this.put(ExceptionTranslationFilter.class, order.next());
this.put(FilterSecurityInterceptor.class, order.next());
this.put(SwitchUserFilter.class, order.next());
}
注意,以上
FilterComparator
中的定義僅作為內(nèi)置過濾器排序的依據(jù),并不等于過濾器的真正注入。
除了內(nèi)置的過濾器外,當(dāng)我們需要添加自定義過濾器的時候,也需要依賴 FilterComparator
對象完成順序的配置。實際開發(fā)時可用以下方法完成向指定順位插入過濾器的功能:http.addFilterAfter
、http.addFilterBefore
、http.addFilter
、http.addFilterAt
(http
為 HttpSecurity
實例)。
另一方面,HttpSecurity
還為各個內(nèi)置過濾器提供了配置接口。例如:針對 HeaderWriterFilter
提供了 headers()
方法,用于獲取或為 HeaderWriterFilter
配置參數(shù),例如設(shè)置響應(yīng)緩存時效 Expires
等 。HttpSecurity
在構(gòu)建時,會根據(jù)已有的配置信息逐一對 Filter
進(jìn)行實例化。HttpSecurity
還完成了對請求地址的配置,通過 RequestMatcherConfigurer
對象使請求地址與 Filter
匹配。
2.2 內(nèi)置過濾器總覽
順序號 | 過濾器名稱 | 簡述 |
---|---|---|
1 | ChannelProcessingFilter | 檢查 web 請求通道,如:http、https |
2 | ConcurrentSessionFilter | 檢查 Session 狀態(tài),更新 Session 最后訪問時間 |
3 | WebAsyncManagerIntegrationFilter | 關(guān)聯(lián) Spring Web 上下文和 Spring Security 上下文 |
4 | SecurityContextPersistenceFilter | 從 Session 構(gòu)建 SecurityContext |
5 | HeaderWriterFilter | 往請求頭或響應(yīng)頭里寫入信息 |
6 | CorsFilter | 跨域請求頭 |
7 | CsrfFilter | 跨站請求偽造 |
8 | LogoutFilter | 注銷過濾器 |
9 | OAuth2AuthorizationRequestRedirectFilter | OAuth2 請求重定向 |
10 | Saml2WebSsoAuthenticationRequestFilter | SAML2 單點登錄認(rèn)證請求過濾器 |
11 | X509AuthenticationFilter | X509 認(rèn)證過濾器 |
12 | AbstractPreAuthenticatedProcessingFilter | 預(yù)認(rèn)證處理 |
13 | CasAuthenticationFilter | 單點認(rèn)證過濾器 |
14 | OAuth2LoginAuthenticationFilter | OAuth2 認(rèn)證過濾器 |
15 | Saml2WebSsoAuthenticationFilter | SAML2 單點登錄認(rèn)證過濾器 |
16 | UsernamePasswordAuthenticationFilter | 用戶名密碼認(rèn)證過濾器 |
17 | ConcurrentSessionFilter | 檢查 Session 狀態(tài),更新 Session 最后訪問時間。第二次出現(xiàn)。 |
18 | OpenIDAuthenticationFilter | Open ID 認(rèn)證過濾器 |
19 | DefaultLoginPageGeneratingFilter | 生成 /login 頁面 |
20 | DefaultLogoutPageGeneratingFilter | 生成 /logout 頁面 |
21 | DigestAuthenticationFilter | 數(shù)字簽名認(rèn)證過濾器 |
22 | BearerTokenAuthenticationFilter | Bearer Token 認(rèn)證過濾器 |
23 | BasicAuthenticationFilter | 基本身份認(rèn)證過濾器 |
24 | RequestCacheAwareFilter | 緩存請求狀態(tài)過濾器 |
25 | SecurityContextHolderAwareRequestFilter | 安全上下文請求輔助過濾器 |
26 | JaasApiIntegrationFilter | JAAS 認(rèn)證授權(quán)過濾器 |
27 | RememberMeAuthenticationFilter | 實現(xiàn)記住我功能 |
28 | AnonymousAuthenticationFilter | 匿名認(rèn)證過濾器 |
29 | OAuth2AuthorizationCodeGrantFilter | OAuth2 認(rèn)證授權(quán)碼 |
30 | SessionManagementFilter | 管理 Session |
31 | ExceptionTranslationFilter | 異常事件處理過濾器 |
32 | FilterSecurityInterceptor | 動態(tài)權(quán)限配置 |
33 | SwitchUserFilter | 切換賬戶 |
2.3 常用內(nèi)置過濾器的具體說明
內(nèi)置過濾器的參數(shù)設(shè)置通過 HttpSecurity 相應(yīng)的配置方法完成。
2.3.1 ChannelProcessingFilter
ChannelProcessingFilter
的用于檢測請求的通道,例如 Http
或 Https
等,可以實現(xiàn)訪問請求在不同通道間的跳轉(zhuǎn)。
ChannelProcessingFilter
的配置通過 HttpSecurity.requiresChannel()
方法獲取。
例如:強(qiáng)制使用 Https 通道訪問。
http.requiresChannel().antMatchers("/users").requiresSecure();
2.3.2 ConcurrentSessionFilter
此過濾器在默認(rèn)情況下出現(xiàn)兩次,其工作內(nèi)容大致分兩步:
- 判斷 Session 是否存在,如果存在則獲取,否則結(jié)束;
- 判斷 Session 是否過期,如果過期則執(zhí)行退出操作,否則更新 Session 時間。
ConcurrentSessionFilter
的配置通過 HttpSecurity.sessionManagement()
方法獲取。
例如:設(shè)置 Session 無效時的跳轉(zhuǎn) URL。
http.sessionManagement().invalidSessionUrl("/login");
2.3.3 WebAsyncManagerIntegrationFilter
WebAsyncManagerIntegrationFilter
用于關(guān)聯(lián) SecurityContext 上下文。
此過濾器無配置公布的方法。
2.3.4 SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
用于從 Session 構(gòu)建 SecurityContext。具體分為兩步:
- 請求開始時,將 SecurityContextRepository 中的 SecurityContext 對象存入 SecurityContextHolder;
- 請求完成時,清理 SecurityContextHolder 中的 SecurityContext 對象,并產(chǎn)生新的 SecurityContext 對象放入到 SecurityContextRepository 中,以保證并發(fā)環(huán)境下的數(shù)據(jù)一致性。
SecurityContextPersistenceFilter
的配置通過 HttpSecurity.securityContext()
方法獲取。
2.3.5 HeaderWriterFilter
HeaderWriterFilter
用于往請求頭或響應(yīng)頭里寫入信息。
HeaderWriterFilter
的配置通過 HttpSecurity.headers()
方法獲取。默認(rèn)支持的 Header 包括:
Header [name: X-Content-Type-Options, values: [nosniff]]
Header [name: X-XSS-Protection, values: [1; mode=block]]
Header [name: Cache-Control, values: [no-cache, no-store, max-age=0, must-revalidate]]
Header [name: Pragma, values: [no-cache]]
Header [name: Expires, values: [0]]
2.3.6 CorsFilter
CorsFilter
用于配置跨域請求策略。當(dāng)一個請求中,來源與目標(biāo)的協(xié)議、主機(jī)名、端口三者任一不同,即為跨域,在實際開發(fā)中如果遇到類似 header is present on the requested resource.
的錯誤時,往往是因為跨域配置不正確導(dǎo)致。
CorsFilter
的配置通過 HttpSecurity.cors()
方法獲取。例如,禁用跨域驗證:
http.cors().disable();
2.3.7 CsrfFilter
CsrfFilter
用于驗證消息來源,防范跨站請求偽造,此項功能需要前端的配合。
CsrfFilter
的配置通過 HttpSecurity.csrf()
方法獲取。例如,禁用 Csrf:
http.csrf().disable();
2.3.8 LogoutFilter
LogoutFilter
用于注銷登錄狀態(tài)。
LogoutFilter
的配置通過 HttpSecurity.logout()
方法獲取。例如,設(shè)置退出后的跳轉(zhuǎn)頁面。
http.logout().logoutSuccessUrl("/login");
2.3.9 UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter
用于處理用戶名、密碼認(rèn)證。
UsernamePasswordAuthenticationFilter
的配置通過 HttpSecurity.formLogin()
方法獲取。例如,設(shè)置用戶名參數(shù)為「mobile」:
http.formLogin().usernameParameter("mobile")
UsernamePasswordAuthenticationFilter
Spring Security 認(rèn)證中較為常用的過濾器,我們會在后續(xù)章節(jié)重點展開。
2.3.10 ExceptionTranslationFilter
ExceptionTranslationFilter
用于異常事件處理。異常事件有前述過濾器拋出,異常共分為 2 類,一類是認(rèn)證異常,另一類是權(quán)限異常。
ExceptionTranslationFilter
的配置通過 HttpSecurity.exceptionHandling()
方法獲取。
ExceptionTranslationFilter
同樣較為常用,將在后續(xù)章節(jié)中重點展開。
3 增加自定義的 Filter
在 HttpSecurity 對象中增加自定義 Filter 可用于實現(xiàn)認(rèn)證方式的擴(kuò)展等場景,擴(kuò)展 Filter 有兩步很重要,第一是實現(xiàn) javax.servlet.Filter 接口;第二是指定新過濾器的位置。
例如,擴(kuò)展自定義接口 SimpleFilter。
- 自定義接口類
public class SimpleFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("In SimpleFilter");
}
}
- 加入到指定位置,比如加在
UsernamePasswordAuthenticationFilter
之前
http.addFilterBefore(new SimpleFilter(), UsernamePasswordAuthenticationFilter.class);
4. 小結(jié)
本節(jié)我們介紹了 Spring Security 過濾器的作用及其順序,主要的知識點有:
- Spring Security 通過擴(kuò)展 Spring 過濾器實現(xiàn)安全相關(guān)業(yè)務(wù)邏輯;
- Spring Security 目前原生實現(xiàn)了 33 個過濾器,每個過濾器有固定的順序及應(yīng)用場景;
- Spring Security 可以的插入自定義的過濾器,并且指定過濾器的位置。
Spring Security 過濾器幾乎是所有安全功能的核心,在后續(xù)章節(jié)中還會圍繞過濾器展開討論。下節(jié)我們將介紹 Spring Security 的異?;厥諜C(jī)制。