服务端开发博客 | 后端架构 · 高并发 · 性能优化 · 微服务实战

服务端开发博客:后端架构、高并发、性能优化与微服务实战教程

软件工程师、项目管理、政企数字化转型十年工作经验,掌握政务数字化各环节工作流程, 咨询|技术服务请留言。 公众号:服务端技术精选
  menu

Spring Cloud Gateway CORS 配置混乱:多服务重复定义跨域易冲突?网关统一拦截+动态响应头注入

公司有 30 多个微服务,每个服务的后端开发都自己配了一份 CORS。结果前端调接口时,有的服务返回了 Access-Control-Allow-Origin,有的没返回,有的返回了 *,有的返回了具体域名。浏览器一看响应头不一致——有的 OPTIONS 预检过了,有的直接被 CORS 策略拦住,前端报了满屏的 blocked by CORS policy。查了半天才发现是服务 A 配了 allowedOrigins(*, example.com),服务 B 配了 allowedOrigins(example.com),服务 C 根本没配。

CORS 应该是网关的活,不是每个微服务的活。所有跨域请求都先到网关,网关统一返回 CORS 响应头,后端服务根本不应该感知到 CORS 的存在。


为什么 CORS 不应该散落在微服务里

CORS 本质是浏览器的一个安全机制——它确保一个域名的脚本不能随意访问另一个域名的资源。网关是所有请求的入口,天然适合做 CORS 的"守门员"。

散落在各个微服务的问题:

  • 配置不一致 —— 30 个服务 30 份 CORS 配置,改一个域名要改 30 个地方
  • OPTIONS 预检穿透 —— OPTIONS 请求是浏览器自动发的,目标服务如果没配 CORS,直接 403
  • 重复的响应头 —— 服务 A 加了 Access-Control-Allow-Origin,网关再加一次,浏览器看到两个可能拒绝

解决思路很简单:微服务全部关闭 CORS,只在网关层配一份。


网关统一 CORS 配置

Spring Cloud Gateway 有 CorsWebFilter,配起来就几行:

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins:
              - "https://admin.example.com"
              - "https://app.example.com"
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTIONS
            allowedHeaders:
              - "Authorization"
              - "Content-Type"
              - "X-Request-ID"
            allowCredentials: true
            maxAge: 3600

每个请求进来,网关自动根据上面的配置生成 CORS 响应头。前端不管调的是订单服务还是支付服务,CORS 响应头都是网关统一返回的——浏览器看到的一致,不会因为"有时候有有时候没有"而报错。


动态响应头:不是所有接口都不该被跨域

网关统一 CORS 能解决大部分问题,但有些场景需要更精细的控制。比如公开 API 允许所有域名跨域,管理后台 API 只允许 admin.example.com 跨域。

这时需要一个动态 CORS 拦截器——根据请求路径匹配不同的跨域策略:

@Component
public class DynamicCorsFilter implements GlobalFilter {

    // 路径 → 允许的源
    private static final Map<Pattern, List<String>> RULES = Map.of(
        Pattern.compile("/api/public/.*"),   List.of("*"),
        Pattern.compile("/api/admin/.*"),    List.of("https://admin.example.com"),
        Pattern.compile("/api/**"),          List.of("https://app.example.com")
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse response = exchange.getResponse();
        String path = exchange.getRequest().getPath().value();

        // 匹配路径 → 设置对应的 CORS 头
        for (var entry : RULES.entrySet()) {
            if (entry.getKey().matcher(path).matches()) {
                String origins = String.join(",", entry.getValue());
                response.getHeaders().add("Access-Control-Allow-Origin", origins);
                response.getHeaders().add("Access-Control-Allow-Methods",
                        "GET,POST,PUT,DELETE,OPTIONS");
                response.getHeaders().add("Access-Control-Allow-Headers",
                        "Authorization,Content-Type");
                response.getHeaders().add("Access-Control-Max-Age", "3600");
                break;
            }
        }

        // OPTIONS 预检直接返回 200,不穿透到微服务
        if (exchange.getRequest().getMethod() == HttpMethod.OPTIONS) {
            response.setStatusCode(HttpStatus.OK);
            return response.setComplete();
        }

        return chain.filter(exchange);
    }
}

关键操作:OPTIONS 请求直接返回 200,不往下游传。 预检请求不需要微服务处理——它唯一的作用就是问"我能不能跨域",网关回答就行了。


后端的 CORS 要关干净

网关配完后,微服务侧必须把 CORS 关了。否则会出现"网关返回了一份 CORS 响应头,微服务又返回了一份,浏览器看到两份"的情况。

Spring Boot 里如果有 @CrossOrigin 注解或 WebMvcConfigurer 里的 CORS 配置,全部移除。或者设一个全局关闭:

// 不要留这个
@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")... // ❌ 删掉
}

总结

CORS 是网关的职责,不是微服务的职责。

三步搞定:

  • 网关统一 CORS —— globalcors 配置一份,所有后端共用
  • 动态响应头 —— 不同路径匹配不同策略,OPTIONS 网关直接返回不穿透
  • 后端关干净 —— 移除所有微服务的 CORS 配置,避免响应头冲突

配完之后,前端跨域问题从"有时报有时不报"变成"永远不报"。


有用的话转给还在每个 Controller 上贴 @CrossOrigin 的同事。


标题:Spring Cloud Gateway CORS 配置混乱:多服务重复定义跨域易冲突?网关统一拦截+动态响应头注入
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/06/28/1782635807058.html
公众号:服务端技术精选
评论
取消