You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 19:43:30