Spring Cloud Gateway (CORS - 脚本无法获取响应主体(原因:CORS Allow Origin Not Matching Origin))
在Spring Cloud Gateway中处理CORS(跨源资源共享)时,可能会遇到“Vary”和“Access-Control-Allow-Origin”两个响应头重复的问题,尤其是当后端服务也配置了CORS支持时。浏览器对“Access-Control-Allow-Origin”头有唯一性要求,这意味着如果响应头中有多个“Access-Control-Allow-Origin”,请求将会被阻止。
原因分析
Spring Cloud Gateway基于Spring WebFlux,所有Web请求首先由DispatcherHandler
处理,它将请求转发给具体的处理器。当请求被转发至后端服务时,后端服务也可能添加了自己的CORS配置,这就会导致响应头中出现多个“Access-Control-Allow-Origin”。
解决方案
解决这个问题有两种主要的方法:
使用DedupeResponseHeader
在Spring Cloud Gateway的配置文件中,可以使用DedupeResponseHeader
来消除重复的响应头。这可以通过添加一个默认过滤器来实现:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods: "*"
default-filters:
- DedupeResponseHeader=Vary Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_FIRST
这里的DedupeResponseHeader
启用了DedupeResponseHeaderGatewayFilterFactory
,它提供了dedupe
方法来按策略处理重复的值。RETAIN_FIRST
策略意味着只保留第一个值,这通常是你自己配置的值,因此能确保正确性和一致性。
自定义GlobalFilter
另一种方法是创建一个自定义的GlobalFilter
,在响应发送前检查并处理重复的响应头。下面是一个示例实现:
@Component
public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
HttpHeaders headers = exchange.getResponse().getHeaders();
headers.entrySet().stream()
.filter(kv -> kv.getValue().size() > 1 && (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS) || kv.getKey().equals(HttpHeaders.VARY)))
.forEach(kv -> {
if (kv.getKey().equals(HttpHeaders.VARY)) {
// 处理Vary头的重复
} else {
// 根据需要处理Access-Control-Allow-Origin和Access-Control-Allow-Credentials的重复
}
});
}));
}
}
在这个自定义的GlobalFilter
中,你可以选择保留第一个、最后一个或者去重后的值,具体取决于你的需求和场景。
结论
为了避免重复的CORS响应头,推荐使用DedupeResponseHeader
配置,因为它更加简洁且易于维护。但是,如果你有更复杂的需求,如不同的策略处理或额外的逻辑,自定义GlobalFilter
则提供更多的灵活性。