03. Spring Security 异常处理
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");
}
}

浙公网安备 33010602011771号