SpringBoot 2.1.2配置CORS后未返回Access-Control响应头问题求助
问题描述
无法在请求响应中获取到CORS访问控制头,期望使用全局配置方案实现端点的精细跨域控制,试过@CrossOrigin注解调试也无法返回对应头信息。
现有适配的控制器如下:
- 状态控制器,包含2个GET端点:
/health/healthcheck
- 上传控制器,包含1个POST端点:
/upload
当前未使用Spring Security,代码编译运行正常,仅跨域场景下异常,使用Swagger测试端点时会报通用CORS错误。用Postman发送OPTIONS请求仅返回Allow: POST, OPTIONS,无任何访问控制头。
相关代码
CorsConfig.java
@Configuration @EnableWebMvc @PropertySource ("classpath:cors.properties") @EnableConfigurationProperties (CorsProperties.class) public class CorsConfig implements WebMvcConfigurer { private final CorsProperties corsProperties; public CorsConfig(CorsProperties corsProperties) { this.corsProperties = corsProperties; } @Override public void addCorsMappings(CorsRegistry registry) { for (CorsModel model : corsProperties.getModels()) { for (String mapping : model.getEndpoints()) { registry.addMapping(mapping) .allowedMethods(removeInputQuotes(model.getAllowedMethods())) .allowedHeaders(removeInputQuotes(model.getAllowedHeaders())) .allowedOrigins(removeInputQuotes(model.getAllowedOrigins())) .allowCredentials(false) // 测试用,添加exposedHeaders未生效 .exposedHeaders("Access-Control-Allow-Origin") .maxAge(32600); } } } private String[] removeInputQuotes(List<String> input) { for (int i = 0; i < input.size(); i++) { input.set(i, input.get(i).replace("\"". "").replace("'", "")); } return input.toArray(new String[0]); } }
调试时配置可正常注入,所有变量赋值正确。
CorsProperties.java
@ConfigurationProperties (prefix = "cors", ignoreUnknownFields = false) public class CorsProperties { public List<CorsModel> models; public List<CorsModel> getModels() { return models; } public void setModels(List<CorsModel> models) { this.models = models; } }
CorsModel.java
public class CorsModel { private List<String> endpoints; private List<String> allowedOrigins; private List<String> allowedHeaders; private List<String> allowedMethods; public List<String> getEndpoints() { return endpoints; } public void setEndpoints(List<String> endpoint) { this.endpoints = endpoint; } public List<String> getAllowedOrigins() { return allowedOrigins; } public void setAllowedOrigins(List<String> allowedOrigins) { this.allowedOrigins = allowedOrigins; } public List<String> getAllowedHeaders() { return allowedHeaders; } public void setAllowedHeaders(List<String> allowedHeaders) { this.allowedHeaders = allowedHeaders; } public List<String> getAllowedMethods() { return allowedMethods; } public void setAllowedMethods(List<String> allowedMethods) { this.allowedMethods = allowedMethods; } }
cors.properties
cors.models[0].allowed-headers=* cors.models[0].allowed-methods=GET,POST,OPTIONS,HEAD cors.models[0].allowed-origins=* cors.models[0].endpoints[0]=/health cors.models[0].endpoints[1]=/healthcheck cors.models[0].endpoints[2]=/swagger** cors.models[0].endpoints[3]=/upload
FileController.java
@RestController public class FileController { @RequestMapping (path = "/upload", method = {RequestMethod.POST}, produces = {MediaType.APPLICATION_JSON_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<?> upload(@RequestPart ("files") List<MultipartFile> trackFiles) { // 业务逻辑省略 return new ResponseEntity<>(HttpStatus.OK); } }
StatusController.java
@RestController public class StatusController { @GetMapping (value = "/health", produces={MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity<HealthCheckResponse> health() { RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); return new ResponseEntity<>(rb.getUptime(), HttpStatus.OK); } }
测试情况
使用curl命令请求端点时响应中无CORS头:
curl -I http://localhost:8080/health curl -I http://localhost:8080/upload
实际返回结果:
HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Thu, 04 Nov 2021 16:40:48 GMT
预期返回结果应包含CORS相关头:
HTTP/1.1 200 Date: Thu, 04 Nov 2021 16:55:38 GMT Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked access-control-allow-origin: * access-control-allow-methods: *
解决方案
按以下步骤排查修改即可解决问题:
- 修复CorsConfig的语法错误
removeInputQuotes方法中字符串替换的参数分隔符错误,将点改为逗号:
// 原错误代码 input.set(i, input.get(i).replace("\"". "").replace("'", "")); // 修改后 input.set(i, input.get(i).replace("\"", "").replace("'", ""));
该语法错误会导致配置加载异常,CORS规则不生效。
2. 修正路径匹配写法
Spring MVC路径匹配规则中/swagger**无法匹配swagger的所有子路径,修改为/swagger/**,如需适配swagger-ui和接口文档路径,可补充/swagger-ui/**、/v3/api-docs/**等路径。
3. 使用CorsFilter替代addCorsMappings配置addCorsMappings的优先级低于框架内置拦截逻辑,部分场景会被覆盖导致不生效,直接注册CorsFilter即可全局生效,在CorsConfig类中添加以下Bean:
@Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); for (CorsModel model : corsProperties.getModels()) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(removeInputQuotes(model.getAllowedHeaders())); config.setAllowedMethods(removeInputQuotes(model.getAllowedMethods())); config.setAllowedOrigins(removeInputQuotes(model.getAllowedOrigins())); config.setAllowCredentials(false); config.setMaxAge(32600L); for (String mapping : model.getEndpoints()) { source.registerCorsConfiguration(mapping, config); } } return new CorsFilter(source); }
- 测试时添加Origin请求头
Spring MVC仅在请求携带Origin头时才会返回CORS相关响应头,curl测试时需添加该头验证:
curl -I -H "Origin: https://example.com" http://localhost:8080/health
未携带Origin头的请求默认不返回CORS头属于正常逻辑。
5. 移除多余配置Access-Control-Allow-Origin是标准CORS响应头,无需手动添加到exposedHeaders中,该行配置可直接删除。
内容的提问来源于stack exchange,提问作者gcerkez
相关产品推荐
相关产品推荐

