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 异常的。

#源码分析

AbstractAuthenticationProcessingFilterdoFilter()上打一个断点

发现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");
    }
}
原文地址:https://www.cnblogs.com/kikochz/p/12892452.html