03. Spring Security 異常處理
參考:
https://blog.csdn.net/yuanlaijike/article/details/80250389
不知道你有沒有注意到,當我們登陸失敗時候,Spring security 幫我們跳轉到了 /login?error
Url,奇怪的是不管是控制台還是網頁上都沒有打印錯誤信息。
這是因為首先 /login?error
是 Spring security 默認的失敗 Url,其次如果你不手動處理這個異常,這個異常是不會被處理的。
#常見異常
我們先來列舉下一些 Spring Security 中常見的異常:
-
UsernameNotFoundException
(用戶不存在) -
DisabledException
(用戶已被禁用) -
BadCredentialsException
(壞的憑據) -
LockedException
(賬戶鎖定) -
AccountExpiredException
(賬戶過期) -
CredentialsExpiredException
(證書過期) -
…
以上列出的這些異常都是 AuthenticationException
的子類,然后我們來看看 Spring security 如何處理 AuthenticationException
異常的。
#源碼分析
再AbstractAuthenticationProcessingFilter
的doFilter()
上打一個斷點
發現try-catch
捕捉了attemptAuthentication(request, response);
拋出的異常
並執行了unsuccessfulAuthentication(request, response, failed);
然后將異常交給SimPleUrlAuthenticationFailureHandeler
轉到方法能明顯的看到發送了/login?error
請求
如果沒有是指defaultFailureUrl
直接返回UNAUTHORIZED
(401)
然后執行saveException(request, exception)
然后判斷 forwardToDestination
,即是否是服務器跳轉,默認使用重定向即客戶端跳轉。
這里可以發現設置了key為SPRING_SECURITY_LAST_EXCEPTION
, value為AuthenticationException
到session域中, 所以我們也就可以通過SPRING_SECURITY_LAST_EXCEPTION
獲取到對應的exception的值
#處理異常
方法一
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.successForwardUrl("/")
//登入失敗跳轉url,重定向
.failureUrl("/fail")
.and()
.logout().permitAll()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.and()
.rememberMe()
.tokenValiditySeconds(60)
.tokenRepository(persistentTokenRepository())
.userDetailsService(userDetailsService)
.and()
.csrf()
.disable();
}
@ResponseBody
@GetMapping("/fail")
public Object fail(HttpServletRequest req, HttpServletResponse resp){
log.info("failure into this ");
//設置編碼防止亂碼
resp.setContentType("application/json;charset=utf-8");
AuthenticationException exception = (AuthenticationException)
req.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
return exception;
}
方法二(推薦)
修改配置類
.successForwardUrl("/")
以POST
方式請求轉發
@ResponseBody
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@PostMapping("/fail")
public String fail() {
return "賬號或密碼錯誤";
}
方法三
修改配置類
.failureHandler(failureHandler())
使用自定的handler, 需要將該類注入到ioc中
- 返回Json
public class FailureHandler implements AuthenticationFailureHandler {
@Autowired
ObjectMapper mapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(mapper.writeValueAsString(exception));
}
}
- 重定向
public class FailureHandler implements AuthenticationFailureHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
ObjectMapper mapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
redirectStrategy.sendRedirect(request,response,"/fail");
}
}