个人博客:无奈何杨(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