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

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: *

解决方案

按以下步骤排查修改即可解决问题:

  1. 修复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);
}
  1. 测试时添加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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 00:06:01