SpringSecurity認證成功、失敗處理器原理分析
首先需要知道的是,SpringSecurity默認的成功、失敗處理器是SavedRequestAwareAuthenticationSuccessHandler
和SimpleUrlAuthenticationFailureHandler
;正常情況下,請求一個資源,在進入到SpringSecurity的認證中,認證成功纔會跳轉到待請求的資源
SavedRequestAwareAuthenticationSuccessHandler源碼分析
認證成功處理器流程:用戶請求一個接口時,會被SpringSecurity攔截。當用戶認證成功後,纔會跳轉到之前用戶想要請求的接口那裏
問題:怎麼知道用戶的請求呢?
SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response)
。這個方法就可以將用戶的請求緩存起來。
假設此時用戶已經認證成功,那麼關於認證成功處理器的調用流程是:
AbstractAuthenticationProcessingFilter#successfulAuthentication
>AuthenticationSuccessHandler#onAuthenticationSuccess
>SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess
SavedRequestAwareAuthenticationSuccessHandler
@Override
//這裏是重寫了該方法,與父類裏的實現不一樣了
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
//將用戶的原始請求緩存住 放到SavedRequest裏
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request
.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
//刪除可能已存儲在服務器中的與身份驗證有關的臨時數據
clearAuthenticationAttributes(request);
String targetUrl = savedRequest.getRedirectUrl();
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
//跳轉到指定的原用戶請求路徑
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
這裏的getRedirectStrategy()是DefaultRedirectStrategy。
DefaultRedirectStrategy
public void sendRedirect(HttpServletRequest request, HttpServletResponse response,
String url) throws IOException {
//處理url
String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
redirectUrl = response.encodeRedirectURL(redirectUrl);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to '" + redirectUrl + "'");
}
//這裏執行跳轉操作
response.sendRedirect(redirectUrl);
}
SavedRequestAwareAuthenticationSuccessHandler源碼分析
當onAuthenticationFailure
方法被調用時,AuthenticationFailureHandler
會根據defaultFailureUrl
的路徑進行跳轉。如果defaultFailureUrl
沒有設置,那麼會發送401錯誤碼和與AuthenticationException
有關的錯誤信息給客戶端。
如果useForward
屬性被設置,那麼RequestDispatcher.forware
將會重定向到目的地而不是跳轉。
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
//如果默認的錯誤跳轉路徑爲空
if (defaultFailureUrl == null) {
logger.debug("No failure URL set, sending 401 Unauthorized error");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Authentication Failed: " + exception.getMessage());
}
else {
//保存異常信息
saveException(request, exception);
//如果forwardToDestination屬性值爲true
if (forwardToDestination) {
logger.debug("Forwarding to " + defaultFailureUrl);
request.getRequestDispatcher(defaultFailureUrl)
.forward(request, response);
}
else {
logger.debug("Redirecting to " + defaultFailureUrl);
redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
}
}
}
//根據forwardToDestination的值 選擇性的將錯誤信息 保存在request[重定向與原請求公用一個request域]中或session中。
protected final void saveException(HttpServletRequest request,
AuthenticationException exception) {
if (forwardToDestination) {
request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
}
else {
HttpSession session = request.getSession(false);
if (session != null || allowSessionCreation) {
request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,
exception);
}
}
}
自定義成功、失敗處理器
成功處理器
@Component
@Log
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info("登錄成功處理器");
//這種方式 即調用父類的方法 是成功之後跳轉到指定url
// super.onAuthenticationSuccess(request, response, authentication);
//下面這種方式 是將認證成功的用戶信息打印到控制檯
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}
}
}
失敗處理器
@Component
@Log
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
//SpringMVC加載JSON解析工具時 注入的 可以直接用
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
log.info("登錄失敗處理器");
//這種方式 即調用父類的方法 是失敗之後跳轉
// super.onAuthenticationFailure(request, response, exception);
//下面這種方式 是將認證失敗的錯誤信息打印到控制檯
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception));
}
}
}
將自定義認證處理器加入到SpringSecurity中
@Configuration
@EnableWebSecurity
public class WebMvcSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationFailureHandler failureHandler;
@Autowired
private MyAuthenticationSuccessHandler successHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler)
.and().authorizeRequests().anyRequest().authenticated();
}
}
這樣認證過程中 就能走到我們自己定義的認證處理器了。
以上,如有錯誤,請指出謝謝。