Spring Security中DisabledException被转为InsufficientAuthenticationException问题排查
问题根因
你遇到的异常转换问题核心是两个配置错误导致的:
- 登录认证失败的异常没有对应处理器接收,导致原始异常丢失
JwtAuthenticationFilter只配置了认证成功处理器,没有配置认证失败处理器。DisabledException属于登录过程中认证逻辑抛出的业务异常,默认情况下如果没有自定义AuthenticationFailureHandler,异常会被过滤器链吞掉,请求会继续向后走到授权校验逻辑,此时SecurityContext中没有有效认证信息,授权校验直接抛出InsufficientAuthenticationException,最后才走到AuthenticationEntryPoint,你拿到的自然是转换后的异常。 - 异常处理逻辑的适用场景搞错了
AuthenticationEntryPoint的作用是处理匿名用户访问受保护资源时抛出的认证异常,比如没有携带token访问私有接口的场景,它根本不会处理登录过程中产生的认证失败异常,你把DisabledException的判断逻辑写到EntryPoint里本来就不会生效。 - 额外小问题:你没有将登录接口配置为公开放行,如果登录接口是POST请求,你当前的配置里只有GET请求的公开路由,登录请求可能还没走到认证逻辑就被授权拦截了,虽然你调试看到已经抛出了
DisabledException说明这次请求走到了认证逻辑,但这个配置漏洞也会导致部分场景下直接返回InsufficientAuthenticationException。
修复方案
1. 新增自定义认证失败处理器
专门处理登录过程中抛出的认证异常:
@Component public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { String errorMessage; ErrorCode errorCode; if (exception instanceof BadCredentialsException) { errorMessage = "Incorrect username or password"; errorCode = ErrorCode.UN_AUTHORIZED; } else if (exception instanceof DisabledException) { errorMessage = "Account verification pending, kindly verify your email address. An email has already been sent to your registered email address. If you have trouble finding it, kindly check the spam folder as well."; errorCode = ErrorCode.DISABLED; } else { errorMessage = exception.getMessage(); errorCode = ErrorCode.UN_AUTHORIZED; } ObjectMapper mapper = new ObjectMapper(); ApiErrorDto apiErrorDto = new ApiErrorDto(); apiErrorDto.setTimestamp(System.currentTimeMillis()); apiErrorDto.setStatus(HttpStatus.UNAUTHORIZED.value()); apiErrorDto.setCode(errorCode.value()); apiErrorDto.setType(errorCode.getReasonPhrase()); apiErrorDto.setError(exception.getClass().getSimpleName()); apiErrorDto.setMessage(errorMessage); apiErrorDto.setPath(request.getRequestURI()); response.setContentType(APPLICATION_JSON_VALUE); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); writer.println(mapper.writeValueAsString(apiErrorDto)); writer.flush(); } }
2. 给JwtAuthenticationFilter绑定失败处理器
修改SecurityConfig中jwtAuthenticationFilter的Bean定义:
@Bean JwtAuthenticationFilter jwtAuthenticationFilter(RestAuthenticationFailureHandler failureHandler) throws Exception { final JwtAuthenticationFilter filter = new JwtAuthenticationFilter(authenticationManager(), jwtConfigProperties, environment); filter.setFilterProcessesUrl(URN_AUTH + URN_LOGIN); filter.setAuthenticationManager(authenticationManager()); filter.setAuthenticationSuccessHandler(successHandler()); // 新增这行绑定失败处理器 filter.setAuthenticationFailureHandler(failureHandler); return filter; }
3. 放行登录接口
修改SecurityConfig的configure方法中的授权规则,添加登录接口的放行配置:
.and().authorizeRequests() // 新增登录接口放行,按实际请求方法调整 .antMatchers(HttpMethod.POST, URN_AUTH + URN_LOGIN).permitAll() .antMatchers(HttpMethod.GET,"** private **").authenticated() .antMatchers(HttpMethod.GET, "**public routes here**").permitAll() .anyRequest().authenticated()
4. 优化EntryPoint的异常判断逻辑
把原来的getClass().equals改为instanceof,避免异常代理、子类继承场景下判断失效:
if (authEx instanceof BadCredentialsException) else if (authEx instanceof DisabledException)
异常定位方法
如果修改后还有问题,可以通过以下方式排查:
- 开启Spring Security debug日志:在配置文件中添加
logging.level.org.springframework.security: DEBUG,查看请求的完整过滤器执行链路,定位异常抛出和转换的节点 - 在
ExceptionTranslationFilter的doFilter方法打断点,这个类是Spring Security统一处理过滤器链异常的入口,可以直接看到捕获的原始异常类型 - 在
AbstractAuthenticationProcessingFilter(你的JwtAuthenticationFilter的父类)的unsuccessfulAuthentication方法打断点,确认认证失败后有没有走到自定义的失败处理器逻辑
内容的提问来源于stack exchange,提问作者The Coder
相关产品推荐
相关产品推荐

