微服务跨域问题|The ‘Access-Control-Allow-Origin‘ header contains multiple values

个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


参考

Spring Cloud Gateway CORS 方案看这篇就够了-腾讯云开发者社区-腾讯云

九种跨域方式实现原理(完整版) - 掘金

什么是跨域,有哪些解决方法

以下为AI回答

跨域是指由于浏览器的安全机制,当一个页面尝试通过 XMLHttpRequest 或 Fetch API 等方式向不同于当前页面源(协议、域名、端口三者之一不同的 URL)发起请求时,会被浏览器阻止,这称为“同源策略”限制。这是为了防止恶意网站通过脚本对其他网站的数据进行非法访问。

解决跨域问题的方法有很多种,以下是常见的几种:

1、CORS(Cross-Origin Resource Sharing)

CORS 是一种 W3C 标准,允许服务器表明哪些其他源可以请求资源。服务器通过设置响应头(如 Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers 等)来允许特定源或者所有源进行跨域请求。客户端(浏览器)在接收到响应后判断是否符合 CORS 规则,若符合则允许数据交互。

2、JSONP(JSON with Padding)

JSONP 是一种非正式传输协议,它利用

3、代理服务器(Proxy Server)

通过在服务器端设置一个代理,使得所有的请求都先经过这个代理服务器,然后由代理服务器转发给目标服务器,这样在浏览器看来,始终是在同一个源下进行通信。

4、document.domain + iframe

对于两个同顶级域名下的页面,可以通过修改各自的 document.domain 属性来允许它们之间的通信。

5、window.postMessage()

HTML5 提供了 window.postMessage 方法,允许来自不同源的脚本采用异步方式进行有限的通信,可以在窗口间发送消息。

6、WebSockets

虽然 WebSocket 连接建立之初也会遇到同源策略限制,但它本身提供了机制来处理跨域连接,服务器可以在握手阶段设置 Origin 头部来控制哪些源可以建立连接。

现代 web 开发中,CORS 是最常用的跨域解决方案,因为它支持更广泛的请求类型(不仅仅是 GET)和更安全的通信模式。然而,在某些特定场景下,如兼容旧版浏览器或者简单的 GET 请求,JSONP 仍然是一个可行的选择。

Spring Cloud Gateway微服务跨域问题

当我们在网关和子服务都配置了CORS允许特定源的跨域请求时,还是会出现以下问题。

The ‘Access-Control-Allow-Origin’ header contains multiple values ‘*, http://localhost:5173’, but only one is allowed.

从提示来看,是Access-Control-Allow-Origin头包含了多个值。

果然,Access-Control-Allow-Origin确实包含了多个值,这就是问题根源,具体的分析可以参考Spring Cloud Gateway CORS 方案看这篇就够了-腾讯云开发者社区-腾讯云文章。

解决方案

既然是重复配置了,那么去掉一个就行了,如下是我的子服务和网关的配置,我是删去了子服务的配置。

当然还有一种方法是在response中去除重复header的。

子服务跨域配置

/**
 * @author wnhyang
 * @date 2024/3/14
 **/
@Configuration
@Slf4j
public class WebConfig {

    /**
     * 跨域配置
     */
    @Bean
    public CorsFilter corsFilter() {
        log.info("[CorsFilter][初始化corsFilter配置]");
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // 设置访问源地址
        config.addAllowedOriginPattern("*");
        // 设置访问源请求头
        config.addAllowedHeader("*");
        // 设置访问源请求方法
        config.addAllowedMethod("*");
        // 有效期 1800秒
        config.setMaxAge(1800L);
        // 添加映射路径,拦截一切请求
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        // 返回新的CorsFilter
        return new CorsFilter(source);
    }
}

网关跨域配置

/**
 * 跨域配置
 *
 * @author wnhyang
 * @date 2023/8/25
 */
@Component
public class GlobalCorsFilter implements WebFilter, Ordered {

    /**
     * 这里为支持的请求头,如果有自定义的header字段请自己添加
     */
    private static final String ALLOWED_HEADERS = "X-Requested-With, Content-Language, Content-Type, Authorization, credential, X-XSRF-TOKEN, isToken, token, Admin-Token, App-Token";
    private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
    private static final String ALLOWED_ORIGIN = "*";
    private static final String ALLOWED_EXPOSE = "*";
    private static final String MAX_AGE = "18000L";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (CorsUtils.isCorsRequest(request)) {
            ServerHttpResponse response = exchange.getResponse();
            HttpHeaders headers = response.getHeaders();
            headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
            headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
            headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
            headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
            headers.add("Access-Control-Max-Age", MAX_AGE);
            headers.add("Access-Control-Allow-Credentials", "true");
            if (request.getMethod() == HttpMethod.OPTIONS) {
                response.setStatusCode(HttpStatus.OK);
                return Mono.empty();
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

写在最后

拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。


个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview

  • 30
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
The 'Access-Control-Allow-Origin' header contains multiple values错误是由于设置了相同的跨域信息导致的。解决办法可以参考以下方法: 1. 使用Spring Cloud Gateway解决双重跨域问题。在配置文件中添加以下内容: ``` spring: cloud: gateway: default-filters: - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE globalcors: cors-configurations: '\[/**\]': allowed-origins: "*" allowed-methods: "*" allowed-headers: "*" allow-credentials: true ``` 这样可以确保只有一个Access-Control-Allow-Origin头信息被设置。 2. 在Nginx配置中添加以下内容,更改客户端请求头,确保只有一个Access-Control-Allow-Origin头信息被设置: ``` location / { proxy_set_header Origin ''; proxy_pass 123.com; add_header Access-Control-Allow-Origin "*" always; } ``` 这样可以通过更改请求头来解决多个Access-Control-Allow-Origin头信息的问题。 请根据具体情况选择适合的解决办法。 #### 引用[.reference_title] - *1* [跨域问题:The ‘Access-Control-Allow-Originheader contains multiple values ‘*‘, but only one is...](https://blog.csdn.net/qq_47768542/article/details/114371791)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [spring cloud解决双重跨域报错The ‘Access-Control-Allow-Originheader contains multiple values , ...](https://blog.csdn.net/qq_37050372/article/details/126140638)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [The ‘Access-Control-Allow-Originheader contains multiple values ‘null, *‘, but only one is ...](https://blog.csdn.net/huang_cheng_zhi/article/details/131101424)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无奈何杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值