Spring Boot 3中JWT认证失败如何返回自定义错误信息?
解决Spring Boot 3中JWT认证自定义异常信息不生效问题
问题现象
在Spring Boot 3环境下实现JWT认证时,当Token验证失败抛出自定义TokenException后,客户端始终收到固定提示"Full authentication is required to access this resource",而非TokenException中定义的自定义错误信息。
问题原因
核心原因是自定义异常未被正确传递到Spring Security的AuthenticationEntryPoint:
- 若
TokenAuthenticationFilter中捕获了TokenException但未正确委托给AuthenticationEntryPoint处理,Spring Security会触发默认的认证失败逻辑,返回固定提示。 - 部分场景下,异常可能被Spring Security包装为其他
AuthenticationException子类,导致无法直接获取自定义消息。
解决方案
1. 修正TokenAuthenticationFilter的异常处理逻辑
确保在Filter中抛出TokenException后,直接调用自定义的AuthenticationEntryPoint处理异常,避免异常被Spring Security默认拦截。示例代码:
@Component public class TokenAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider jwtTokenProvider; private final AuthenticationEntryPoint restAuthenticationEntryPoint; // 构造注入依赖 public TokenAuthenticationFilter(JwtTokenProvider jwtTokenProvider, AuthenticationEntryPoint restAuthenticationEntryPoint) { this.jwtTokenProvider = jwtTokenProvider; this.restAuthenticationEntryPoint = restAuthenticationEntryPoint; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String authToken = extractTokenFromRequest(request); if (authToken != null && !authToken.isBlank()) { // 调用JWT验证方法,失败则抛出TokenException jwtTokenProvider.decodeJWT(authToken); // 验证成功后设置认证信息到SecurityContext(根据自身逻辑实现) setAuthenticationToContext(request); } filterChain.doFilter(request, response); } catch (TokenException e) { // 清除上下文,避免残留认证信息 SecurityContextHolder.clearContext(); // 委托自定义AuthenticationEntryPoint处理异常 restAuthenticationEntryPoint.commence(request, response, e); } } // 辅助方法:从请求头中提取Token(示例逻辑) private String extractTokenFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } // 辅助方法:设置认证信息到SecurityContext(根据自身业务实现) private void setAuthenticationToContext(HttpServletRequest request) { // 示例逻辑,需替换为实际的用户认证逻辑 Authentication authentication = new UsernamePasswordAuthenticationToken("username", null, Collections.emptyList()); SecurityContextHolder.getContext().setAuthentication(authentication); } }
2. 优化AuthenticationEntryPoint的异常消息处理
在自定义的restAuthenticationEntryPoint中,直接判断异常类型,确保能获取到TokenException的自定义消息:
@Bean public AuthenticationEntryPoint restAuthenticationEntryPoint(ObjectMapper objectMapper) { return (request, response, error) -> { log.info("restAuthenticationEntryPoint: {}", error.getMessage(), error); Map<String, Object> errorObject = new HashMap<>(); int errorCode = HttpStatus.UNAUTHORIZED.value(); // 优先使用自定义TokenException的消息 String errorMessage = error.getMessage(); if (error instanceof TokenException) { errorMessage = error.getMessage(); } else { // 非自定义异常时返回默认提示 errorMessage = "Full authentication is required to access this resource"; } errorObject.put("message", errorMessage); errorObject.put("error", HttpStatus.UNAUTHORIZED.getReasonPhrase()); errorObject.put("code", errorCode); errorObject.put("timestamp", new Timestamp(System.currentTimeMillis())); response.setContentType("application/json;charset=UTF-8"); response.setStatus(errorCode); response.getWriter().write(objectMapper.writeValueAsString(errorObject)); }; }
3. 确保SecurityFilterChain配置正确
保持原有的SecurityFilterChain配置,但需确保TokenAuthenticationFilter被正确注入并添加到过滤器链中:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, TokenAuthenticationFilter tokenAuthenticationFilter) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) .exceptionHandling(e -> e.authenticationEntryPoint(restAuthenticationEntryPoint(objectMapper))) .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }
验证
完成上述修改后,测试不同的Token异常场景(过期、签名错误等),客户端将收到对应的自定义错误消息,而非固定提示。
内容的提问来源于stack exchange,提问作者Forece85
相关产品推荐
相关产品推荐

