springcloud gateway解决跨域问题
/** * 跨域允许 */ @Configuration public class CorsConfig { @Bean public WebFilter corsFilter() { return (ServerWebExchange ctx, WebFilterChain chain) -> { ServerHttpRequest request = ctx.getRequest(); if (CorsUtils.isCorsRequest(request)) { ServerHttpResponse response = ctx.getResponse(); HttpHeaders headers = response.getHeaders(); headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*"); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, ""); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*"); headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600"); if (request.getMethod() == HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } } return chain.filter(ctx); }; } }
参考博客:https://blog.csdn.net/a294634473/article/details/90715903
以上只是简单的处理,如果再网关和后端都进行了跨域配置,在返回的请求头中可能包含多个跨域的信息,那样同样会让请求出现异常,所以在上面的处理后,要将重复的跨域头信息给删除掉
在高一点的springcloud gateway中包含了DedupeResponseHeaderGatewayFilterFactory,可以进行过滤,如果低版本,没有这个类,自行从高版本中拷贝过来作为一个实现类,然后添加上配置
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin Access-Control-Allow-Methods Access-Control-Allow-Headers Vary
附件代码:
GwCorsFilter.java
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.util.pattern.PathPatternParser; @Configuration public class GwCorsFilter { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 允许cookies跨域 config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin config.addAllowedHeader("*");// #允许访问的头信息,*表示全部 config.addAllowedMethod("*");// 允许提交请求的方法类型,*表示全部允许 config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了 org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source = new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } }
DedupeResponseHeaderGatewayFilterFactory.java
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.HttpHeaders; import org.springframework.web.server.ServerWebExchange; import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Mono; /* Use case: Both your legacy backend and your API gateway add CORS header values. So, your consumer ends up with Access-Control-Allow-Credentials: true, true Access-Control-Allow-Origin: https://musk.mars, https://musk.mars (The one from the gateway will be the first of the two.) To fix, add DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin Configuration parameters: - name String representing response header names, space separated. Required. - strategy RETAIN_FIRST - Default. Retain the first value only. RETAIN_LAST - Retain the last value only. RETAIN_UNIQUE - Retain all unique values in the order of their first encounter. Example 1 default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Response header Access-Control-Allow-Credentials: true, false Modified response header Access-Control-Allow-Credentials: true Example 2 default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials, RETAIN_LAST Response header Access-Control-Allow-Credentials: true, false Modified response header Access-Control-Allow-Credentials: false Example 3 default-filters: - DedupeResponseHeader=Access-Control-Allow-Credentials, RETAIN_UNIQUE Response header Access-Control-Allow-Credentials: true, true Modified response header Access-Control-Allow-Credentials: true */ /** * @author Vitaliy Pavlyuk */ @Slf4j public class DedupeResponseHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory<DedupeResponseHeaderGatewayFilterFactory.Config> { private static final String STRATEGY_KEY = "strategy"; public DedupeResponseHeaderGatewayFilterFactory() { super(Config.class); } @Override public List<String> shortcutFieldOrder() { return Arrays.asList(NAME_KEY, STRATEGY_KEY); } @Override public GatewayFilter apply(Config config) { return new GatewayFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).then(Mono.fromRunnable( () -> dedupe(exchange.getResponse().getHeaders(), config))); } }; } public enum Strategy { /** * Default: Retain the first value only. */ RETAIN_FIRST, /** * Retain the last value only. */ RETAIN_LAST, /** * Retain all unique values in the order of their first encounter. */ RETAIN_UNIQUE } void dedupe(HttpHeaders headers, Config config) { String names = config.getName(); Strategy strategy = config.getStrategy(); if (headers == null || names == null || strategy == null) { return; } for (String name : names.split(" ")) { dedupe(headers, name.trim(), strategy); } } private void dedupe(HttpHeaders headers, String name, Strategy strategy) { List<String> values = headers.get(name); log.info("{}={}",name,values); if (values == null || values.size() <= 1) { return; } switch (strategy) { case RETAIN_FIRST: headers.set(name, values.get(0)); break; case RETAIN_LAST: headers.set(name, values.get(values.size() - 1)); break; case RETAIN_UNIQUE: headers.put(name, values.stream().distinct().collect(Collectors.toList())); break; default: break; } } public static class Config extends AbstractGatewayFilterFactory.NameConfig { private Strategy strategy = Strategy.RETAIN_FIRST; public Strategy getStrategy() { return strategy; } public Config setStrategy(Strategy strategy) { this.strategy = strategy; return this; } } }