Java 17 Spring Maven应用返回403而非404问题求助
问题原因
你的问题出在Spring Security的拦截逻辑和JWT过滤器的处理顺序上:
- Spring Security默认会在Spring MVC处理请求前拦截所有未匹配
permitAll的请求,强制要求认证,导致不存在的接口请求被提前拦截返回403,而非让Spring MVC返回404。 - 如果你的
JwtAuthenticationFilter在检测到无Token/无效Token时直接返回403响应,也会跳过后续路径存在性检查,提前返回错误。
解决方案
1. 调整SecurityFilterChain配置,区分路径不存在与未认证场景
修改securityFilterChain方法,添加错误请求权限,并自定义认证入口逻辑来判断请求路径是否存在:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .dispatcherTypeMatchers(DispatcherType.ERROR).permitAll() // 允许Spring MVC错误请求绕过认证 .requestMatchers("/api/v1/city/**").permitAll() .requestMatchers("/api/v1/auth/**").permitAll() .anyRequest().authenticated() ) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { // 检查请求是否对应存在的Spring MVC接口 HandlerMapping handlerMapping = request.getServletContext().getAttribute(RequestMappingHandlerMapping.class.getName()); try { HandlerExecutionChain handler = handlerMapping.getHandler(request); if (handler == null) { // 无匹配接口,返回404 response.sendError(HttpServletResponse.SC_NOT_FOUND, "Not Found"); } else { // 接口存在但未认证,返回403 response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden"); } } catch (Exception e) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden"); } }) ); return http.build(); }
2. 修正JwtAuthenticationFilter逻辑,避免提前拦截无Token请求
确保JWT过滤器在无Token或Token无效时,仅跳过认证逻辑,继续执行过滤器链,让Spring MVC完成路径存在性检查:
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtService jwtService; private final UserDetailsService userDetailsService; public JwtAuthenticationFilter(JwtService jwtService, UserDetailsService userDetailsService) { this.jwtService = jwtService; this.userDetailsService = userDetailsService; } @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain ) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); final String jwt; final String userEmail; if (authHeader == null || !authHeader.startsWith("Bearer ")) { // 无Token,继续执行过滤器链,交给Spring MVC判断路径是否存在 filterChain.doFilter(request, response); return; } jwt = authHeader.substring(7); userEmail = jwtService.extractUsername(jwt); if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); if (jwtService.isTokenValid(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } // 继续执行后续过滤器 filterChain.doFilter(request, response); } }
验证效果
修改完成后:
- 请求存在但未携带有效Token的接口:返回403
- 请求不存在的接口:返回404
- 请求
/api/v1/city/**或/api/v1/auth/**下的接口:正常访问无需认证
内容的提问来源于stack exchange,提问作者Gabriel John
相关产品推荐
相关产品推荐

