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.

