Spring Boot微服务InvalidTokenException返回500错误如何处理?
问题根因
InvalidTokenException是在Spring Security过滤器链执行阶段抛出的,请求还未进入Controller层,因此默认的@ControllerAdvice全局异常处理器无法捕获该异常,最终会被BasicErrorController处理返回500状态码。
解决方案
以下提供两种常用的可落地处理方案:
方案1:配置资源服务器专属异常处理器(推荐)
Spring Security的OAuth2资源服务器本身提供了认证异常、权限异常的处理扩展点,直接配置即可覆盖这类场景:
- 自定义认证入口点,处理所有认证相关异常:
@Component public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 判断根异常是否为无效令牌异常 if (authException.getCause() instanceof InvalidTokenException) { response.getWriter().write("{\"code\":401,\"message\":\"无效的访问令牌,请重新登录\"}"); return; } response.getWriter().write("{\"code\":401,\"message\":\"请先完成身份认证\"}"); } }
- 自定义权限拒绝处理器,处理权限不足场景:
@Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.getWriter().write("{\"code\":403,\"message\":\"权限不足,无法访问当前资源\"}"); } }
- 将两个处理器注入到安全配置中(Spring Boot 2.7+ 推荐使用
SecurityFilterChain配置):
@Configuration public class ResourceSecurityConfig { @Autowired private CustomAuthenticationEntryPoint authenticationEntryPoint; @Autowired private CustomAccessDeniedHandler accessDeniedHandler; @Bean public SecurityFilterChain resourceSecurityChain(HttpSecurity http) throws Exception { http.authorizeRequests() // 放行获取令牌的接口 .antMatchers("/oauth/token").permitAll() .anyRequest().authenticated() .and() .exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint) .accessDeniedHandler(accessDeniedHandler) .and() .oauth2ResourceServer().jwt(); // 按你实际的token校验方式配置即可 return http.build(); } }
如果使用的是旧版本的ResourceServerConfigurerAdapter,直接在configure(HttpSecurity http)方法中添加上述exceptionHandling配置即可。
方案2:全局异常过滤器兜底
如果需要统一处理所有过滤器层抛出的异常,可以新增一个优先级最高的异常过滤器,放在Spring Security过滤器链之前捕获所有异常:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class GlobalSecurityExceptionFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException { try { chain.doFilter(request, response); } catch (Exception e) { HttpServletResponse resp = (HttpServletResponse) response; resp.setContentType("application/json;charset=utf-8"); // 匹配无效令牌异常 if (e instanceof InvalidTokenException || (e instanceof AuthenticationException && e.getCause() instanceof InvalidTokenException)) { resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); resp.getWriter().write("{\"code\":401,\"message\":\"无效的访问令牌,请重新登录\"}"); return; } // 其他异常统一处理逻辑可自行扩展 resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); resp.getWriter().write("{\"code\":500,\"message\":\"服务器内部错误\"}"); } } }
注意事项
异常抛出时通常会被Spring Security做一层包装,判断类型时优先检查根异常getCause()即可避免漏匹配的情况。
内容的提问来源于stack exchange,提问作者ExplainSpringMagicToMePls
相关产品推荐
相关产品推荐

