1. 前言
上一節(jié)中我們介紹了 Spring Security 中內(nèi)置的安全過濾器,本節(jié)將介紹 Spring Security 的另一個基礎概念:「異常處理」。
這里所指異常是 Spring Security 在認證和鑒權過程中捕獲的異常,也就是訪問目標資源發(fā)生錯誤時的原因,Spring Security 以異常的形式處理「認證錯誤」和「權限錯誤」,并將結(jié)果傳回請求方。
學習 Spring Security 的異?;厥諜C制,有助于在開發(fā)和應用過程中排查問題。
2. 異常處理流程
Spring Security 的認證、授權異常在過濾器校驗過程中產(chǎn)生,并在 ExceptionTranslationFilter
中接收并進行處理,其流程如下:
ExceptionTranslationFilter
過濾器首先像其他過濾器一樣,調(diào)用過濾器鏈的執(zhí)行方法FilterChain.doFilter(request, response)
啟動過濾處理;- 如果當前的用戶沒有通過認證或者因為其他原因在執(zhí)行過程中拋出了
AuthenticationException
異常,此時將開啟「認證流程」:- 清空
SecurityContextHolder
對象; - 并將原始請求信息「request」保存到
RequestCache
對象中; - 使用
AuthenticationEntryPoint
對象存儲的認證地址,向客戶端索要身份證明。例如,使用瀏覽器登錄的用戶,將瀏覽器地址重定向到/login
或者回傳一個WWW-Authenticate
認證請求頭。
- 清空
- 如果當前用戶身份信息已確認,但是沒有訪問權限,則會產(chǎn)生
AccessDeniedException
異常,然后訪問被拒絕。繼續(xù)執(zhí)行拒絕處理AccessDeniedHandler
。
假如認證過程中沒有產(chǎn)生「認證異常」或者「權限異?!?,ExceptionTranslationFilter
則不做任何處理。
3. 異常的種類
3.1 認證異常
認證異常是在認證階段拋出的異常,其主要的實現(xiàn)類包括:
-
AccountStatusException
出現(xiàn)在賬戶狀態(tài)異常時候,比如認證憑據(jù)過期了、賬戶被鎖定了等。
-
ActiveDirectoryAuthenticationException
出現(xiàn)在 AD 域認證異常時。
-
AuthenticationCancelledException
出現(xiàn)在 OpenID 認證時,認證狀態(tài)被取消。
-
AuthenticationCredentialsNotFoundException
出現(xiàn)在無法找到認證憑證時,即
SecurityContext
實例中找不到Authentication
對象。 -
AuthenticationServiceException
出現(xiàn)在認證時遇到了后臺錯誤。
-
BadCredentialsException
出現(xiàn)在憑據(jù)檢查失敗,比如賬戶被禁用時。
-
InsufficientAuthenticationException
出現(xiàn)在以獲得憑據(jù),但憑據(jù)不被信任的情況。
-
NonceExpiredException
出現(xiàn)在數(shù)字證書異常時。
-
OAuth2AuthenticationException
出現(xiàn)在 OAuth2 認證異常時。
-
PreAuthenticatedCredentialsNotFoundException
出現(xiàn)在預認證憑據(jù)未找到時。
-
ProviderNotFoundException
出現(xiàn)在當前認證方式不被支持時。
-
RememberMeAuthenticationException
出現(xiàn)在記住我認證失敗時。
-
Saml2AuthenticationException
出現(xiàn)在 Saml2 認證失敗時。
-
SessionAuthenticationException
出現(xiàn)在會話異常時,比如當前用戶創(chuàng)建的會話已經(jīng)超過系統(tǒng)容量。
-
UsernameNotFoundException
出現(xiàn)在找不到用戶時。
3.2 權限異常
權限異常是在訪問資源階段拋出的異常,其主要的實現(xiàn)類包括:
-
AuthorizationServiceException
當鑒權請求無法完成或者,比如找不到目標方法時拋出此異常。
-
org.springframework.security.web.server.csrf.CsrfException
當 「CsrfToken」異常或缺失時拋出此異常。
-
org.springframework.security.web.csrf.CsrfException
當 「CsrfToken」異?;蛉笔r拋出此異常。
4. 自定義異常處理
當我們需要自定義異常處理時,需要在 HttpSecurity
對象的 exceptionHandling() 方法獲取異常處理的配置入口 ExceptionHandlingConfigurer
,并使用該類提供的 AuthenticationEntryPoint
和 AccessDeniedHandler
參數(shù)來配置異常處理:
AuthenticationEntryPoint
該類用來統(tǒng)一處理AuthenticationException
異常;AccessDeniedHandler
該類用來統(tǒng)一處理AccessDeniedException
異常。
4.1 自定義 AuthenticationEntryPoint
假設我們想統(tǒng)一異常 json 響應。
public class JSONAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
// 實現(xiàn)個性化業(yè)務邏輯 ...
// 配置返回值
HashMap<String, String> map = new HashMap<>(2);
map.put("uri", request.getRequestURI());
map.put("msg", "認證失敗");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
String resBody = objectMapper.writeValueAsString(map);
PrintWriter printWriter = response.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
}
4.2 實現(xiàn) AccessDeniedHandler
同樣假設我們想統(tǒng)一異常 json 響應。
public class JSONAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
// 實現(xiàn)個性化業(yè)務邏輯 ...
// 配置返回值
HashMap<String, String> map = new HashMap<>(2);
map.put("uri", request.getRequestURI());
map.put("msg", "認證失敗");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
String resBody = objectMapper.writeValueAsString(map);
PrintWriter printWriter = response.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
}
5. 小結(jié)
本節(jié)我們介紹了 Spring Security 中的異常回收機制:
- Spring Security 的異常處理是通過「安全過濾器」方式實現(xiàn)的;
- Spring Security 的異常分為兩大類,分別是「身份異?!购汀笝嘞蕻惓!梗?/li>
- Spring Security 可以通過修改配置自定義異常處理。
至此,關于 Spring Security 基礎部分就告一段了,下節(jié)開始我們進入應用環(huán)節(jié),討論 Spring Security 的第一個重要模塊「認證」。