SpringCloud网关Gateway跨域处理,兼容IE

头像
码农笔录
2021-04-19 后端 阅读量 10714

跨域是一个前后端分离开发无法避免的坑,尤其是要兼容ie。之前单项目的时候,都是在后台直接配置cors就好了,或者在nginx中配置,但是微服务要是挨个都配置,代码量大,也不是很优雅。所以我们一般都会在网关配置跨域处理,以下是我的方案,项目亲测可用。

springboot版本:2.3.9.RELEASE

@Configuration public class CorsWebFilter implements WebFilter { private static final String ALL = "*"; private static final String MAX_AGE = "18000L"; @Override public Mono<Void> filter(ServerWebExchange ctx, WebFilterChain chain) { ServerHttpRequest request = ctx.getRequest(); if (!CorsUtils.isCorsRequest(request)) { return chain.filter(ctx); } String path = request.getPath().value(); ServerHttpResponse response = ctx.getResponse(); if ("/favicon.ico".equals(path)) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } HttpHeaders requestHeaders = request.getHeaders(); HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod(); HttpHeaders headers = response.getHeaders(); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin()); if (requestMethod != null) { headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders().toString().replace("[", "").replace("]", "")); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name()); } headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL); headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE); if (request.getMethod() == HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } return chain.filter(ctx); } }

一些注意事项:
1.设置ACCESS_CONTROL_ALLOW_HEADERS的时候不能把请求的数组AccessControlRequestHeaders直接给塞进去,否则ie会报错,这里需要转成String类型。
2.OPTION请求过来的时候会带着AccessControlRequestHeaders,我们在OPTION返回的时候设置跨域的请求头,下次正式请求过来的时候,就不需要设置了,因为OPTION预检验是通过的。
3.ACCESS_CONTROL_ALLOW_HEADERS设置*的话,ie上是会报错的。报错异常信息为:Access-Control-Allow-Headers 列表中不存在请求标头 x-requested-with。
4.如果发现设置的跨域响应头是重复的,那么你需要去重,代码在下方。

@Component public class CorsResponseHeaderFilter implements GlobalFilter, Ordered { @Override public int getOrder() { // 指定位于 NettyWriteResponseFilter 处理完响应体后移除重复 CORS 响应头 return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1; } @Override @SuppressWarnings("serial") public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).then(Mono.defer(() -> { 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))) .forEach(kv -> kv.setValue(new ArrayList<String>() {{ add(kv.getValue().get(0)); }})); return chain.filter(exchange); })); } }