Spring Security 在 Servlet 請求中實現鑒權
1. 前言
Servlet 是 Java Web 應用的最常見場景,Spring Security 對 Servlet 的權限認定過程實現了規范化和流程化。
本節主要討論在 Servlet 應用中,如何通過 Spring Security 實現鑒權。
2. Servlet 權限控制的流程
Servlet 鑒權主要圍繞著 FilterSecurityInterceptor
類展開,該類作為一個安全過濾器,被放置在 FilterChainProxy
中。
圖 1. Servlet 請求鑒權流程
具體流程如下:
FilterSecurityInterceptor
從SecurityContextHolder
中獲取Authentication
對象;FilterSecurityInterceptor
從HttpServletRequest
、HttpServletREsponse
、FilterChain
中創建FilterInvocation
對象;- 將創建的
FilterInvocation
對象傳遞給SecurityMetadataSource
用來獲取ConfigAttribute
對象集合; - 最后,將
Authentication
、FilterInvocation
和ConfigAttribute
對象傳遞給AccessDecisionManager
實例驗證權限:- 如果驗證失敗,將拋出
AccessDeniedException
異常,并由ExceptionTranslationFilter
接收并處理; - 如果驗證通過,
FilterSecurityInterceptor
將控制權交還給FilterChain
,使程序繼續執行。
- 如果驗證失敗,將拋出
3. Servlet 權限控制的配置實現
默認情況下,Spring Security 的權限要求所有的請求都需要首先通過認證。相當于以下配置描述:
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
);
}
如果我們需要配置 Spring Security 對不同請求有不同的處理規則時,可通過以下方式修改配置:
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests(authorize -> authorize
.mvcMatchers("/resources/**", "/signup", "/about").permitAll()
.mvcMatchers("/admin/**").hasRole("ADMIN")
.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().denyAll()
);
}
以上這段代碼實現了如下功能:
- 指定了多種規則,每種規則按照其配置的順序決定優先級;
- 匹配了多個 URL 規則,對于
/resources
、/signup
、/about
允許任何用戶訪問; - 任何以
/admin/
開頭的地址都要求用戶具有管理員權限ROLE_ADMIN
,其中ROLE_
前綴是 Spring Security 默認添加的,用戶無需做特殊處理; - 任何以
/db/
為開頭的地址需要同時擁有ROLE_ADMIN
和ROLE_DBA
角色; - 沒有被上述 URL 地址匹配的地址都將被禁止訪問,加上這一條非常有助于提升系統的安全性。
4. 小結
本節討論了如何用 Spring Security 規范化 Servlet 鑒權過程:
- Spring Security 對 Servlet 請求的權限控制,主要依賴于
FilterSecurityInterceptor
實現; - Spring Security 默認對每一個 Servlet 請求都要求用戶已通過認證;
- Spring Security 支持對 Servlet 的不同 URL 規則配置不同的權限規則。
下節我們討論如何通過「訪問控制表達式」實現多樣化的鑒權規則。