You need to enable JavaScript to run this app. Spring/React访问带JWT授权的admin端点时CORS预检失败问题排查
优惠活动
大模型
产品
解决方案
定价
更多

Spring/React访问带JWT授权的admin端点时CORS预检失败问题排查

核心问题根因

两个独立问题叠加导致CORS预检流程失败:

  • Spring Security的过滤器链优先级高于Spring MVC的跨域处理逻辑
  • 前端代码违规设置了不符合浏览器规范的请求头
具体问题拆解与修复方案
  • Spring Security 拦截预检请求导致CORS配置失效
    你目前在启动类中通过WebMvcConfigurer配置的全局跨域规则、Controller上标注的@CrossOrigin注解,全部属于Spring MVC层面的跨域处理逻辑,这套逻辑运行在DispatcherServlet的内部流程中。而Spring Security的过滤器是在Servlet容器层面的最外层执行的,当携带自定义JWT头的跨域请求触发OPTIONS预检时,该请求不会携带身份凭证,会直接被Security的鉴权逻辑拦截返回403,根本走不到MVC层的跨域判断环节,所有MVC层的跨域配置自然失效。
    这也是为什么你的跨域配置在其他Controller上能生效——那些接口对应的路径匹配了permitAll规则,OPTIONS预检请求不会被Security拦截,可以顺利走到MVC层的CORS处理流程;而/api/v1/admin/**路径被配置为仅ADMIN角色可访问,不带凭证的OPTIONS预检请求会直接被Security拦截返回403,走不到后续的CORS判断环节。
    正确修复方式是将跨域处理逻辑前置到Spring Security的过滤器链中,让Security直接处理CORS请求,不再拦截预检请求。修改SpringSecurityConfig的配置如下:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 开启Security层面的CORS支持,优先处理跨域请求
        http.cors().and()
            .csrf().disable()
            .authorizeRequests()
                // 原有鉴权规则完全保留
                .antMatchers("/api/v1/customers/**").hasRole("CUSTOMER")
                .antMatchers("/api/v1/admin/**").hasRole("ADMIN")
                .antMatchers("/api/v1/users/**").hasRole("USER")
                .antMatchers("/api/v1/manufacturer/**").hasRole("MANUFACTURER")
                .antMatchers("/api/v1/authenticated").authenticated()
                .antMatchers("/api/v1/authenticate").permitAll()
                .anyRequest().permitAll()
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
    
    // 注入Security专用的CORS配置源
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 指定允许的前端源,避免通配符与凭证携带冲突
        configuration.setAllowedOrigins(List.of("http://localhost:3000"));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    

    配置完成后,Security会在过滤器链最外层先处理CORS预检请求,直接返回合法的200响应,不会将OPTIONS请求判定为需要鉴权的接口拦截。

  • 前端请求头不符合浏览器规范触发二次报错
    你目前在axios请求中手动添加的Access-Control-Allow-Origin: *属于后端响应头,作用是告诉浏览器允许跨域访问,不是需要前端发送给后端的请求头。当请求携带这个非标准头时,浏览器会触发更严格的预检校验规则,最终报出NS_ERROR_DOM_BAD_URI错误。
    前端修复方式为直接删除请求头中多余的Access-Control-Allow-Origin配置,仅保留鉴权所需的请求头:

    const result = await axios.get(url, {
        headers : {
            "Authorization" : `Bearer ${token}`,
            'Content-Type': 'application/json'
        }
    });
    
  • package.json代理配置不生效的原因
    Create React App内置的代理配置仅在前端发起同源相对路径请求时才会生效。你在代码中写死了完整的后端地址http://localhost:8080,请求本身已经是跨域请求,不会触发代理转发逻辑。如果需要使用代理绕开CORS限制,需要将前端请求地址改为相对路径(例如仅写/api/v1/admin/orders/,不拼接后端域名),代理才会将请求转发到后端地址。

注意事项
  • 不要使用Access-Control-Allow-Origin: *同时搭配allowCredentials=true,这是浏览器明确禁止的组合,会直接拦截响应。本地开发阶段建议将允许的源明确指定为http://localhost:3000,即可同时支持凭证携带。
  • 全局CORS配置生效后,不需要在Controller、方法上重复标注多层@CrossOrigin注解,重复配置不会提升兼容性,反而可能造成规则冲突。

内容的提问来源于stack exchange,提问作者Ronald v. E.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 15:06:03