您的位置:首页 > 其它

浏览器跨域问题解决办法

2017-05-26 10:37 246 查看

前言

之前在一个项目中,调用了别的部门的接口,但是由于存在跨域问题,只能进行简单的本地数据模拟调试,然后再把资源给对方进行联调。这样的方法肯定不是最佳方案,正好最近看到一篇文章,说到的就是跨域问题的解决办法,所以进行了学习和整理。

同源政策

首先说说同源政策,最初,它的含义是指,A网页设置的Cookie,B网页不能打开,除非这两个网页“同源”。所谓“同源”,就是三个相同:

协议相同
域名相同
端口相同
同源政策的目的就是为了防止信息泄露。

如果非同源,共有三种行为会受到限制:

Cookie、LocalStorage和IndexDB无法读取;
DOM无法获得;
Ajax请求不能发送。
下来说说如何针对上面三种情况进行跨域。

Cookie

Cookie是服务器写入浏览器的一小段信息,只有同源的网页才能共享,但是,两个页面一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享Cookie。

举例来说,A网页是http://hr.163.com/a.html

B网页是http://study.163.com/b.html

主要设置相同的document.domain,两个网页就可以共享Cookie。

我们在两个页面都设置:

document.domain = '163.com';
1
1
然后在a网页设置cookie:

document.cookie = "test1=hello";
1
1
就可以在b网页读到这个cookie了:

var allCookie = document.cookie;
1
1
我们也可以在服务器进行配置,指定Cookie的所属域名为一级域名,这样,二级三级域名不用做任何设置,都可以读取这个cookie:

Set-Cookie: key=value; domain=.163.com; path=/
1
1

iframe

如果两个网页不同源,就无法拿到对方的DOM,典型的例子就是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

父窗口获取子窗口:

document.getElementById("myIFrame").contentWindow.document
1
1
子窗口获取父窗口:

window.parent.document.body
1
1
如果两个窗口一级域名相同,只是二级域名不同,那么设置document.domain即可

如果是完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题。

片段标示符
window.name
跨文档通信API

片段标示符

片段标示符是URL的#后面的部分,比如

http://163.com/a.html#fragment的#fragment,如果只是改变片段标示符,页面不会重新刷新。

父窗口可以把信息,写入子窗口的片段标示符:

var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;
1
2
1
2
子窗口可以通过监听hashchange事件得到通知:

window.onhashchange = checkMessage;

function checkMessage() {
var message = window.location.hash;
// ...
}
1
2
3
4
5
6
1
2
3
4
5
6
同样的,子窗口也可以改变父窗口的片段标示符:

parent.location.href= target + "#" + hash;
1
1

window.name

浏览器窗口有window.name属性,这个属性最大的特点是,无论是否同源,只要在同一个窗口里,前一个页面设置了这个属性,后一个网页可以读取它。

父窗口先打开一个子窗口,载入一个不同源的页面,该网页将信息写入window.name属性:

window.name = data;
1
1
子窗口跳回一个与主窗口同域的网址:

location = 'http://parent.url.com/xx.html';
1
1
然后,主窗口就可以读取子窗口的window.name了:

var data = document.getElementById('myFrame').contentWindow.name;
1
1
优点是window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性值的变化,影响页面性能。

window.postMessage

HTML5的全新API:跨文档通信API。

父窗口
http://aaa.com

向子窗口http://bbb.com

发消息,调用postMessage方法就可以了:

var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');
1
2
1
2
posetMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即”协议 + 域名 + 端口”。也
4000
可以设为*,表示不限制域名,向所有窗口发送。

子窗口向父窗口发送消息的写法类似:

window.opener.postMessage('Nice to see you', 'http://aaa.com');
1
1
父窗口和子窗口都可以通过message事件,监听对方的消息:

window.addEventListener('message', function(e) {
console.log(e.data);
},false);
1
2
3
1
2
3
message事件的事件对象event,提供以下三个属性:

event.source:发送信息的窗口
event.origin:信息发向的网址
event.data:信息内容
下面可以看看几个例子:

子窗口通过event.source属性引用父窗口,然后发送信息

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
event.source.postMessage('Nice to see you!', '*');
}
1
2
3
4
1
2
3
4
event.origin属性可以过滤不是发送本窗口的信息

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
if (event.origin !== 'http://aaa.com') return;
if (event.data === 'Hello World') {
event.source.postMessage('Hello', event.origin);
} else {
console.log(event.data);
}
}
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9

AJAX

ajax的跨域需求很常见的。现在有三种方法来解决:

JSONP
WebSocket
CORS

JSONP

JSONP是服务器与客户端跨域通信的常用方法,最大的特点是简单适用,老式浏览器全部支持,服务器改造非常小。

它的思想是,页面通过添加一个
<script>
元素,向服务器请求JSON数据,服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入
<script>
元素,由它向跨源网址发出请求:

function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}

window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
服务器收到这个请求后,会将数据放在回调函数的参数位置返回:

foo({
"ip": "8.8.8.8"
});
1
2
3
1
2
3
JSONP**只能发GET请求**,这点需要注意。

WebSocket

WebSocket是一种通信协议,使用ws://(非加密)

和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨域通信。

下面举例子,浏览器发出的websocket请求头:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com[/code]1 2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
其中Origin字段,表示该请求的请求源,服务器可以判断这个字段,判断是否允许本次通信,如果同意,做出反应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
1
2
3
4
5
6
1
2
3
4
5
6

CORS

CORS可以允许任何类型的请求,是W3C的新标准。

首先,在请求头信息中添加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...
1
2
3
4
5
6
1
2
3
4
5
6
如果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
1
2
3
4
5
1
2
3
4
5
如果不在允许范围内,也会返回正常的HTTP回应,但是**没有**Access-Control-Allow-Origin字段,浏览器发现后,就知道出错了,从而抛出错误,被XMLHttpRequest的onerror回调函数捕获。

根据详细的字段配置,可以看看这篇文章

,讲的非常详细。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: