今天在SpringSecurity框架 弄个验证码生成的功能,然后就一直不行,一直跳向登陆地址,也没有报错信息,找了大半天什么都试过,后来才发现时你未认证时如果你得代码抛出了AuthenticationException及其子类时他会跳像登陆地址,异常信息也会丢掉!遇到这种坑B问题只能傻眼了!
首先我得代码是这样的
/**
* 根据类型获取验证码处理器
* @param type 验证码类型
* @return
*/
public ValidateCodeProcessor findValidateCodeProcessor(String type){
String name = type.toLowerCase() + ValidateCodeProcessor.class.getSimpleName();
ValidateCodeProcessor validateCodeProcessor = validateCodeProcessorMap.get(name);
if(validateCodeProcessor == null){
throw new ValidateCodeException("验证码处理器" + name + "不存在");
}
return validateCodeProcessor;
}
此时如果 validateCodeProcessor 为空则会抛出一个异常,这个异常是AuthenticationException的子类,注意是子类是子类,然后我这没写对,抛出了这个异常!
这个时候就会来到
org.springframework.security.web.access.ExceptionTranslationFilter#doFilter
的catch代码块
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
chain.doFilter(request, response);
}
catch (IOException ex) {
throw ex;
}
catch (Exception ex) {
// spring会把你抛出的异常包装好几层,所以下面这几段代码在拿你抛的那个异常,也就是上面的ValidateCodeException
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
// 这里肯定不等于空,不等于空,他就去调用Spring Security的异常处理器
if (ase != null) {
handleSpringSecurityException(request, response, chain, ase);
}
}
}
private void handleSpringSecurityException(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, RuntimeException exception)
throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
sendStartAuthentication(request, response, chain,
(AuthenticationException) exception);
}
}
protected void sendStartAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
// 将认证信息设置为空
SecurityContextHolder.getContext().setAuthentication(null);
// 缓存这个请求和响应,等会他会创建一个新的请求,
requestCache.saveRequest(request, response);
// 这个是关键,他会掉这个方法,使用的是这个类 LoginUrlAuthenticationEntryPoint
authenticationEntryPoint.commence(request, response, reason);
}
现在来看 LoginUrlAuthenticationEntryPoint 这个类做了什么
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
String redirectUrl = null;
// 这里他会获取你配置的登陆地址,
redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
// 重定向到登陆页
redirectStrategy.sendRedirect(request, response, redirectUrl);
}
看完上面的解释,所以如果你的接口是想让他匿名访问(未认证时)的,那么你千万千万别抛AuthenticationException的子类异常,比如获取验证码,我这就是获取验证码的问题!不然你抛出的异常信息也不会打印,直接就跳转到登陆地址!