Angular对接带JWT的Spring Boot服务PUT请求403跨域预检失败排查
核心问题诱因
- 配置逻辑存在低级断层:写了完整的跨域规则参数后,路由注册环节根本没有使用这份配置——代码重新实例化了一个空白的CorsConfiguration对象,靠默认值生成规则,默认规则仅放行GET、HEAD、POST三类请求,完全不包含PUT、OPTIONS的权限,前面写的配置全部无效。
- 规则与浏览器规范不匹配:开启
setAllowCredentials(true)时,浏览器CORS机制不支持*作为通配源,即便配置了全源放行规则,浏览器也会判定响应无效,不会通过跨域校验。 - 安全拦截链路存在缺口:Spring Security的授权规则没有对OPTIONS类型的预检请求做放行配置,这类请求不会携带JWT鉴权头,会被后续的认证过滤器拦截返回401状态,响应中自然不会携带要求的
Access-Control-Allow-Origin头,直接触发浏览器的CORS阻断报错。
补充说明:GET、POST请求未触发异常,是因为不带自定义请求头的简单请求不会触发浏览器的OPTIONS预检流程,可直接通过链路访问;PUT请求因携带JWT相关的自定义请求头,属于非简单请求,必须先完成预检校验,因此会暴露上述配置问题。
调整修复方案
- 修正配置传递逻辑:将自定义的跨域配置实例传入
UrlBasedCorsConfigurationSource的注册方法,不再使用无意义的默认空配置。 - 匹配凭证模式下的源规则:替换
*通配源,明确写入需要放行的前端源地址(包含本地开发地址、正式部署后的前端地址)。 - 补充预检请求放行规则:在Spring Security的授权链路中,显式允许所有路径下的OPTIONS请求直接通过,不经过JWT鉴权拦截。
- 补齐请求头放行范围:显式配置允许跨域携带的请求头,覆盖JWT认证需要的
Authorization头。
修正后的关键配置代码
@Bean CorsConfigurationSource configurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // 替换*通配源,匹配allowCredentials的浏览器规范要求 configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200")); configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS")); // 显式放行所有请求头,覆盖JWT认证需要的Authorization头 configuration.setAllowedHeaders(Arrays.asList("*")); configuration.setAllowCredentials(true); // 配置预检缓存时长,减少重复预检请求开销 configuration.setMaxAge(3600L); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 修正注册逻辑,传入已配置好的规则实例 source.registerCorsConfiguration("/**", configuration); return source; } @Override protected void configure(HttpSecurity http) throws Exception { http.cors().configurationSource(configurationSource()) .and() .csrf().disable() .authorizeRequests() // 新增规则:放行所有路径下的OPTIONS预检请求 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers(AUTH_WHITELIST).permitAll() .antMatchers(HttpMethod.POST, "/authenticate/signup").permitAll() .anyRequest().authenticated() .and() .exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) .and() .addFilter(new AuthenticationFilter(authenticationManager())) .addFilter(new AuthorizationFilter(authenticationManager())) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .formLogin().usernameParameter("email"); }
配置生效后可先通过浏览器开发者工具的网络面板,查看PUT请求触发的OPTIONS预检响应是否正常携带
Access-Control-Allow-Origin、Access-Control-Allow-Methods头,确认校验通过后,后续的正式PUT请求即可正常携带JWT完成鉴权交互。
内容的提问来源于stack exchange,提问作者Sabeeh Ul Haq
相关产品推荐
相关产品推荐

