只要与当前浏览器访问的url不同(协议,域名,端口号),就会产生跨域。
方法1、通过配置文件解决
在GateWay配置文件中
spring:
cloud:
# gateway的配置
gateway:
# 跨域配置
globalcors:
cors-configurations:
'[/**]': # 允许跨域访问的资源
allowedOrigins: "*" #跨域允许来源
allowedHeaders: "*"
allowedMethods: "*"
allowCredentials: true
maxAge: 360000
正常情况下基于以上配置即可,但是由于目前的项目的下游微服务也配置了可以跨域的相关配置,这就导致返回的ResponseHeader中有多重属性,这个多重属性浏览器是不认的。所以基于此的处理方法,把下游的所有配置都取消,但是下游服务数量又太多,所以通过查询找到了以下的方案,参考https://github.com/spring-cloud/spring-cloud-gateway/issues/728
spring:
cloud:
# gateway的配置
gateway:
# 此处不注掉,会导致gateway转发websocket服务的时候出现自动断开的情况,报504错误
# discovery:
# locator:
# enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
# 跨域配置
globalcors:
cors-configurations:
'[/**]': # 允许跨域访问的资源
allowedOrigins: "*" #跨域允许来源
allowedHeaders: "*"
allowedMethods: "*"
allowCredentials: true
maxAge: 360000
# 加了以下配置会导致gateway转发websocket服务的时候出现自动断开的情况,报504错误
default-filters:
- DedupeResponseHeader=Vary Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_FIRST
在配置文件中添加上面的过滤器,这个过滤器的作用是剔除重复的响应头。
方法2、通过配置类解决
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*"); // 允许的method
config.addAllowedOrigin("*"); // 允许的来源
config.addAllowedHeader("*"); // 允许的请求头参数
config.setAllowCredentials(true);
config.setMaxAge(360000L);
// 运行访问的资源
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
如果要处理上面提到的重复响应头,使用下面的方法
@Component
public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
private static final Logger logger = LoggerFactory.getLogger(CorsResponseHeaderFilter.class);
private static final String ANY = "*";
@Override
public int getOrder() {
// 指定此过滤器位于NettyWriteResponseFilter之后
// 即待处理完响应体后接着处理响应头
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
}
@Override
@SuppressWarnings("serial")
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
exchange.getResponse().getHeaders().entrySet().stream()
.filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
.filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)
|| kv.getKey().equals(HttpHeaders.VARY)))
.forEach(kv ->
{
// Vary只需要去重即可
if(kv.getKey().equals(HttpHeaders.VARY))
kv.setValue(kv.getValue().stream().distinct().collect(Collectors.toList()));
else{
List<String> value = new ArrayList<>();
if(kv.getValue().contains(ANY)){ //如果包含*,则取*
value.add(ANY);
kv.setValue(value);
}else{
value.add(kv.getValue().get(0)); // 否则默认取第一个
kv.setValue(value);
}
}
});
}));
}
}