SpringCloudGateway Access-Allow-Origin header contains multiple values“*, *”,but only one is allowed

朴灿烈づ我的快乐病毒、 2022-12-28 06:11 114阅读 0赞

问题现象

在spring cloud gateway网关中加入了跨域支持,但是报了如下错误:

  1. The Access-Control-Allow-Origin header contains multiple values “*, *”, but only one is allowed.

错误显示设置了两次Access-Control-Allow-Origin:*。

问题原因

经查看请求报文和错误确定是 Access-Control-Allow-Origin 出现了多个值(浏览器目前是不允许的),其原因是是在 gateway 中配置过了 Access-Control-Allow-Origin,后端服务的开发人员也配置了 Access-Control-Allow-Origin,导致 response 在响应的时候 Access-Control-Allow-Origin 出现了多个值(不管两个值相同还是不同浏览器目前都会报错),截图如下:
在这里插入图片描述

解决该问题的思路

  • 可以将所有后端服务的跨域处理都去除,交网关统一处理
  • 可以将网关的处理去除(那么后端所有服务都需要添加)
  • 在网关做去重处理,只保留一个值响应给浏览器(这是本文选择的处理方法)

解决方案

1 设置跨域:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.http.HttpHeaders;
  4. import org.springframework.http.HttpMethod;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.http.server.reactive.ServerHttpRequest;
  7. import org.springframework.http.server.reactive.ServerHttpResponse;
  8. import org.springframework.web.cors.reactive.CorsUtils;
  9. import org.springframework.web.server.ServerWebExchange;
  10. import org.springframework.web.server.WebFilter;
  11. import org.springframework.web.server.WebFilterChain;
  12. import reactor.core.publisher.Mono;
  13. /** * @author mazhen * @className CorsConfig * @Description TODO * @date 2020/12/14 19:09 */
  14. @Configuration
  15. public class GatewayCorsConfig {
  16. private static final String MAX_AGE = "18000L";
  17. @Bean
  18. public WebFilter corsFilter() {
  19. return (ServerWebExchange ctx, WebFilterChain chain) -> {
  20. ServerHttpRequest request = ctx.getRequest();
  21. if (CorsUtils.isCorsRequest(request)) {
  22. HttpHeaders requestHeaders = request.getHeaders();
  23. ServerHttpResponse response = ctx.getResponse();
  24. HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
  25. HttpHeaders headers = response.getHeaders();
  26. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
  27. headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders
  28. .getAccessControlRequestHeaders());
  29. if(requestMethod != null){
  30. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
  31. }
  32. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
  33. headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
  34. headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
  35. if (request.getMethod() == HttpMethod.OPTIONS) {
  36. response.setStatusCode(HttpStatus.OK);
  37. return Mono.empty();
  38. }
  39. }
  40. return chain.filter(ctx);
  41. };
  42. }
  43. }

2 修改响应头,去掉*:

  1. package com.cloudpath.gateway.portal.config.cors;
  2. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
  3. import org.springframework.cloud.gateway.filter.GlobalFilter;
  4. import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
  5. import org.springframework.core.Ordered;
  6. import org.springframework.http.HttpHeaders;
  7. import org.springframework.stereotype.Component;
  8. import org.springframework.web.server.ServerWebExchange;
  9. import reactor.core.publisher.Mono;
  10. import java.util.ArrayList;
  11. /** * @author mazhen * @className CorsResponseHeaderFilter * @Description TODO * @date 2020/12/14 19:16 */
  12. @Component("corsResponseHeaderFilter")
  13. public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
  14. @Override
  15. public int getOrder() {
  16. // 指定此过滤器位于NettyWriteResponseFilter之后
  17. // 即待处理完响应体后接着处理响应头
  18. return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
  19. }
  20. /** * Process the Web request and (optionally) delegate to the next {@code WebFilter} * through the given {@link GatewayFilterChain}. * * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete */
  21. @Override
  22. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  23. return chain.filter(exchange).then(Mono.defer(() -> {
  24. exchange.getResponse().getHeaders().entrySet().stream()
  25. .filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
  26. .filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
  27. || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)))
  28. .forEach(kv -> {
  29. kv.setValue(new ArrayList<String>() { {
  30. add(kv.getValue().get(0));
  31. }});
  32. });
  33. return chain.filter(exchange);
  34. }));
  35. }
  36. }

到这里,就解决了问题!

发表评论

表情:
评论列表 (有 0 条评论,114人围观)

还没有评论,来说两句吧...

相关阅读