一、AuthenticationEntryPoint簡介
AuthenticationEntryPoint是Spring Security Web一個概念模型接口,顧名思義,他所建模的概念是:“認證入口點”。
它在用戶請求處理過程中遇到認證異常時,被ExceptionTranslationFilter用於開啟特定認證方案(authentication schema)的認證流程。
該接口只定義了一個方法:
void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException;
這里參數request是遇到了認證異常authException用戶請求,response是將要返回給客戶的相應,方法commence實現,也就是相應的認證方案邏輯會修改response並返回給用戶引導用戶進入認證流程。
在該方法被調用前,ExceptionTranslationFilter會做好如下工作:填充屬性HttpSession
,使用屬性名稱為AbstractAuthenticationProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY
二、AuthenticationEntryPoint
源代碼
package org.springframework.security.web; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.access.ExceptionTranslationFilter; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface AuthenticationEntryPoint { /** * Commences an authentication scheme. * * ExceptionTranslationFilter will populate the HttpSession * attribute named * AbstractAuthenticationProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY * with the requested target URL before calling this method. * * Implementations should modify the headers on the ServletResponse as * necessary to commence the authentication process. * * @param request that resulted in an AuthenticationException * @param response so that the user agent can begin authentication * @param authException that caused the invocation * */
void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException; }
Spring Security Web 為AuthenticationEntryPoint提供了一些內置實現:
1、Http403ForbiddenEntryPoint:設置響應狀態字為403,並非觸發一個真正的認證流程。通常在一個預驗證(pre-authenticated authentication)已經得出結論需要拒絕用戶請求的情況被用於拒絕用戶請求。
2、HttpStatusEntryPoint:設置特定的響應狀態字,並非觸發一個真正的認證流程。
3、LoginUrlAuthenticationEntryPoint:根據配置計算出登錄頁面url,將用戶重定向到該登錄頁面從而開始一個認證流程。
4、BasicAuthenticationEntryPoint:對應標准Http Basic認證流程的觸發動作,向響應寫入狀態字401和頭部WWW-Authenticate:"Basic realm="xxx"觸發標准Http Basic認證流程。
5、DigestAuthenticationEntryPoint:對應標准Http Digest認證流程的觸發動作,向響應寫入狀態字401和頭部WWW-Authenticate:"Digest realm="xxx"觸發標准Http Digest認證流程。
6、DelegatingAuthenticationEntryPoint:這是一個代理,將認證任務委托給所代理的多個AuthenticationEntryPoint對象,其中一個被標記為缺省AuthenticationEntryPoint。
三、自定義認證返回
public class ExamAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { response.setStatus(HttpStatus.UNAUTHORIZED.value()); OperationInfo info = OperationInfo.failure("請登錄后操作"); HttpResponseUtil.setResponseMessage(response, info); } }
1、實現 AuthenticationEntryPoint 類,覆寫 commence 方法。
2、設置ExamAuthenticationEntryPoint
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf()...... .exceptionHandling().authenticationEntryPoint(new ExamAuthenticationEntryPoint()) ...... }
四、自定義AccessDeineHandler
AuthenticationEntryPoint 用來解決匿名用戶訪問無權限資源時的異常 —— 也就是未授權的問題
AccessDeineHandler 用來解決認證過的用戶訪問無權限資源時的異常 —— 也就是權限不足的問題
public class CustomAccessDeineHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { response.setCharacterEncoding("utf-8"); response.setContentType("text/javascript;charset=utf-8"); response.getWriter().print(JSONObject.toJSONString(RestMsg.error("沒有訪問權限!"))); } }
//添加自定義異常入口,處理accessdeine異常 http.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint()) .accessDeniedHandler(new CustomAccessDeineHandler());
// 禁用緩存 http.headers().cacheControl(); // 添加JWT filter http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); //添加未授權處理 http.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint()); //權限不足處理 http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());