不知道你有没有注意到,当我们登陆失败时候,Spring security 帮我们跳转到了 /login?error
Url,奇怪的是不管是控制台还是网页上都没有打印错误信息。
这是因为首先 /login?error
是 Spring security 默认的失败 Url,其次如果你不手动处理这个异常,这个异常是不会被处理的。
一、常见异常
我们先来列举下一些 Spring Security 中常见的异常:
UsernameNotFoundException
(用户不存在)DisabledException
(用户已被禁用)BadCredentialsException
(坏的凭据)LockedException
(账户锁定)AccountExpiredException
(账户过期)CredentialsExpiredException
(证书过期)- …
以上列出的这些异常都是 AuthenticationException
的子类,然后我们来看看 Spring security 如何处理 AuthenticationException
异常的。
二、源码分析
我们知道异常处理一般在过滤器中处理,我们在 AbstractAuthenticationProcessingFilter
中找到了对 AuthenticationException
的处理:
(1)在 doFilter()
中,捕捉了 AuthenticationException
异常,并交给了 unsuccessfulAuthentication()
处理。
(2)在 unsuccessfulAuthentication()
中,转交给了 SimpleUrlAuthenticationFailureHandler
类的 onAuthenticationFailure()
处理。
(3)在 onAuthenticationFailure()
中,首先判断有没有设置 defaultFailureUrl
。
-
如果没有设置,直接返回 401 错误,即
HttpStatus.UNAUTHORIZED
的值。 -
如果设置了,首先执行
saveException()
方法。然后判断forwardToDestination
,即是否是服务器跳转,默认使用重定向即客户端跳转。
(4)在 saveException()
方法中,首先判断forwardToDestination
,如果使用服务器跳转则写入 Request,客户端跳转则写入 Session。写入名为 SPRING_SECURITY_LAST_EXCEPTION
,值为 AuthenticationException
。
至此 Spring security 完成了异常处理,总结一下流程:
–> AbstractAuthenticationProcessingFilter.doFilter()
–> AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication()
–> SimpleUrlAuthenticationFailureHandler.onAuthenticationFailure()
–> SimpleUrlAuthenticationFailureHandler.saveException()
三、处理异常
上面源码说了那么多,真正处理起来很简单,我们只需要指定错误的url,然后再该方法中对异常进行处理即可。
(1)指定错误Url,WebSecurityConfig
中添加 .failureUrl("/login/error")
1 | ... |
(2)在 Controller 中处理异常
1 |
|
我们首先获取了 session 中的 SPRING_SECURITY_LAST_EXCEPTION
。为了演示,我只是简单的将错误信息返回给了页面。运行程序,当我们输入错误密码时: