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");
}
}