个人博客:无奈何杨(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
是一种非正式传输协议,它利用 script
标签不受同源策略限制的特点,通过回调函数的形式来加载跨域数据。JSONP
只支持 GET 请求,并且要求服务器端配合提供 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.
image
从提示来看,是 Access-Control-Allow-Origin
头包含了多个值。
果然,Access-Control-Allow-Origin 确实包含了多个值,这就是问题根源,具体的分析可以参考 Spring Cloud
Gateway CORS 方案看这篇就够了 - 腾讯云开发者社区 - 腾讯云 文章。
image
解决方案
既然是重复配置了,那么去掉一个就行了,如下是我的子服务和网关的配置,我是删去了子服务的配置。
当然还有一种方法是在 response
中去除重复 header
的。
子服务跨域配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @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("*" ); config.setMaxAge(1800L ); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (); source.registerCorsConfiguration("/**" , config); return new CorsFilter (source); } }
网关跨域配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Component public class GlobalCorsFilter implements WebFilter , Ordered { 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