[BOM]跨域问题解决方案汇总

发布时间 2024-01-05 09:46:51作者: 夕苜19

参考:

https://blog.csdn.net/huzhenv5/article/details/104884760

https://juejin.cn/post/6844903521163182088

 

什么是同源:域名、协议、端口均为相同。

 

 

1. jsonp

动态创建js标签进行跨域。

只能实现get请求。在调用失败的时候不会返回任何状态码。存在安全问题。

 

2. document.domain + iframe 跨域

默认情况下,document.domain存放的是载入文档的服务器的主机名,可以手动设置这个属性,设置该属性有两个特点:

(1)只能设置成当前域名,或者当前域的二级域名

(比如,document.domain为 www.baidu.com,可以设置成 baidu.com)

(2)任何对document.domain的赋值操作,包括

 document.domain = document.domain都会导致端口号被重写为 null

 

通过这两个特点,我们就可以实现二级域名相同的不同域之间的跨域dom访问(就算端口不同也可以)

 

3. window.name + iframe 跨域

全局对象属性,只能实现get请求

(1)每个窗口都有独立的window.name与之对应;

(2)在一个窗口的生命周期中(被关闭前),窗口载入的所有页面同时共享一个window.name,每个页面对window.name都有读写的权限;

(3)window.name一直存在与当前窗口,即使是有新的页面载入也不会改变window.name的值;

(4)window.name可以存储不超过2M的数据,数据格式按需自定义。

 

可以在a页面中使用代码将需要获取的数据写到window.name中,再在b页面中使用iframe打开a页面,获取到window.name。

 

4. location.hash + iframe 跨域

全局对象属性,只能实现get请求

 

5. window.postMessage()

可以安全地实现跨源通信,语法:

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow

其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。

message

将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。

targetOrigin

通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串""(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。

transfer (可选)

是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

 

 

6. 跨域资源共享 CORS

主流解决方案。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

 

简单请求:

1)请求方式为HEAD、POST 或者 GET

2)http头信息不超出一下字段:

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)

 

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。 下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1

Origin: http://api.bob.com

Host: api.alice.com

Accept-Language: en-US

Connection: keep-alive

User-Agent: Mozilla/5.0

...

 

Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。 浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。

注意,这种错误无法通过状态码识别,HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

   Access-Control-Allow-Origin: http://api.bob.com

   Access-Control-Allow-Credentials: true

   Access-Control-Expose-Headers: FooBar

   Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头:

(1)Access-Control-Allow-Origin: 必须。

它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求

(2)Access-Control-Allow-Credentials: 可选。

它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(3)Access-Control-Expose-Headers: 可选。

CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。

如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

 

上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

另一方面,开发者必须在AJAX请求中打开withCredentials属性。

但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials。

 

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

 

// 前端设置是否带cookie

xhr.withCredentials = true;

 

xhr.open('post', 'http://www.domain2.com:8080/login', true);

xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

xhr.send('user=admin');

 

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4 && xhr.status == 200) {

        alert(xhr.responseText);

    }

};

 

// jquery

$.ajax({

    ...

   xhrFields: {

       withCredentials: true    // 前端设置是否带cookie

   },

   crossDomain: true,   // 会让请求头中包含跨域的额外信息,但不会含cookie

    ...

});

 

 

非简单请求:

对服务器有特殊要求。如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

 

 7. WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

 

 8. Node代理跨域

node中间件实现跨域代理,是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

利用node + express + http-proxy-middleware搭建一个proxy服务器

 

9. Nginx