SpringBoot集成Spring Security(3)——异常处理

有了前两章的铺垫,现在我们学习一下springsecurity是怎样处理异常的

SpringBoot集成Spring Security(1)——入门程序

SpringBoot集成Spring Security(2)——自动登录

不知道你有没有注意到,当我们登陆失败时候,spring security帮我们跳转到了/login?error,奇怪的是不管是控制台还是网页上都没有打印错误信息。

目录

Step1 常见异常

Step2 源码分析

Step3 处理异常


SpringBoot集成Spring Security(3)——异常处理

这是因为首先/login?errorspring security默认的失败url,其次如果你不手动处理这个异常,这个异常是不会被处理的

Step1 常见异常

我们先来列举下一些常见的异常:

(1)UsernameNotFoundException(用户不存在)

(2)DisabledException(用户已被禁用)

(3)BadCredentialsException(坏的凭据)

(4)LockedException(账户锁定)

(5)AccountExpiredException (账户过期)

(6)CredentialsExpiredException(证书过期)

(7) …

Step2 源码分析

我们知道异常处理一般在过滤器中处理,我们在AbstractAuthenticationProcessingFilter中找到了对AuthenticationException的处理:

(1)在doFilter()中,捕捉了AuthenticationException异常,并交给了unsuccessfulAuthentication()处理。
SpringBoot集成Spring Security(3)——异常处理

(2)在unsuccessfulAuthentication()中,转交给了SimpleUrlAuthenticationFailureHandler类的onAuthenticationFailure()处理。

SpringBoot集成Spring Security(3)——异常处理

(3)在onAuthenticationFailure()中,首先判断有没有设置defaultFailureUrl

① 如果没有设置,直接返回401错误,即HttpStatus.UNAUTHORIZED的值。

② 如果设置了:首先执行saveException()方法。然后判断forwardToDestination,即是否是服务器跳转,默认使用重定向即客户端跳转。
SpringBoot集成Spring Security(3)——异常处理(4)在saveException()方法中,首先判断forwardToDestination,如果使用服务器跳转则写入Request,客户端跳转则写入Session。写入名为SPRING_SECURITY_LAST_EXCEPTION,值为AuthenticationException
SpringBoot集成Spring Security(3)——异常处理

至此spring security完成了异常处理,总结一下:

–> AbstractAuthenticationProcessingFilter.doFilter() 
–> AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication() 
–> SimpleUrlAuthenticationFailureHandler.onAuthenticationFailure() 
–> SimpleUrlAuthenticationFailureHandler.saveException()


Step3 处理异常

上面源码说了那么多,真正处理起来很简单,我们只需要指定错误的url,然后再该方法中对异常进行处理即可。

(1)指定错误Url,WebSecurityConfig中添加.failureUrl("/login/error")

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 如果有允许匿名的url,填在下面
//                .antMatchers().permitAll()
                .anyRequest().authenticated()
                .and()
                // 设置登陆页
                .formLogin().loginPage("/login")
                // 设置登陆成功页
                .defaultSuccessUrl("/").permitAll()
                // 登录失败Url
                .failureUrl("/login/error")
                // 自定义登陆用户名和密码参数,默认为username和password
//                .usernameParameter("username")
//                .passwordParameter("password")
                .and()
                .logout().permitAll()
                // 自动登录
                .and().rememberMe()
                    .tokenRepository(persistentTokenRepository())
                    // 有效时间:单位s
                    .tokenValiditySeconds(60)
                    .userDetailsService(userDetailsService);

        // 关闭CSRF跨域
        http.csrf().disable();
    }

(2)在Controller中处理异常

@RequestMapping("/login/error")
public void loginError(HttpServletRequest request, HttpServletResponse response) {
    response.setContentType("text/html;charset=utf-8");
    AuthenticationException exception =
            (AuthenticationException)request.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
    try {
        response.getWriter().write(exception.toString());
    }catch (IOException e) {
        e.printStackTrace();
    }
}

我们首先获取了session中的SPRING_SECURITY_LAST_EXCEPTION。为了演示,我只是简单的将错误信息返回给了页面。

运行程序,当我们输入错误密码时:

SpringBoot集成Spring Security(3)——异常处理