一、什么是跨域?
一般来说,向一个非同源网站发送请求获取数据,就是跨域请求。
//跨域错误提示
Failed to load http://luna.58.com/api/getcity: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342'; is therefore not allowed access.
同源策略——跨域问题产生原因
浏览器为了网络信息安全所采取的的措施,限制资源访问权限。
来源相同是指:协议、主机(域名),端口号,全部相同。
http://www.example.com/dir/page.html
协议:http
域名:www.example.com
端口:80(默认端口,可以省略)
- http://www.example.com/dir2/other.html:同源
- http://example.com/dir/other.html:不同源(域名不同)(域名必须完全相同)
- http://www.example.com:81/dir/other.html:不同源(端口不同)
域名级别
一级域名(顶级域名):baidu.com
二级域名:www.baidu.com
三级域名:wangshangyingxiao.club.1688.com
同源策略限制范围
//如果非同源,共有三种行为受到限制
(1)cookie、localstorage、IndexDB 无法读取
(2)DOM结构无法获得
(3)AJAX请求不能发送
二、跨域通信方法
1、设置document.domain
此方法只适用于:两个网页一级域名相同,二级域名不同(或者三级域名不同)
(1)共享cookie;(2)iframe窗口,获取dom结构。
实现方法:两个网页设置相同的document.domain,设置为一级域名,即可共享cookie
情景1:实现cookie共享,两个网页一级域名相同,二级域名不同。
cookie是服务器写入浏览器的信息,只有同源的网页才可以共享。
//举例说明:
//设置相同的domain
document.domain = 'example.com';
//设置后
docoment.domain //两个页面的值均为 example.com
//A页面,设置cookie
document.cookie = 'test=hello';
//B页面,获取cookie
document.cookie //cookie的值中包含设置的test=hell
服务器也可以在设置cookie时,指定cookie的所属域名为一级域名, 这样的话,二级域名和三级域名不用做任何设置,都可以读取到这个cookie。
情景2:获取iframe的Dom结构,两个网页一级域名相同,二级域名不同。
iframe Dom结构:只有同源的页面才可以相互通信,获取DOM结构。
//举例说明:
//设置前,非同源页面,父窗口获取子窗口dom,报错
document.getElementById('#myIframe').contentWindow.document
//Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.
//设置前,非同源页面,子窗口获取父窗口dom,报错
window.parent.document.body
//Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.
//设置相同的domain
//设置后,即可获取Dom
docoment.domain //两个页面的值均为 example.com
2、片段识别符 (fragment identifier)
针对完全不同源的主页面与iframe页面,解决跨域通信的问题。将通信内容写入片段标识符中。
片段识别符是指URL中#号后面的部分,例如:http://example.com/x.html#fragment的#fragment。
只更改片段标识符,页面不会重新刷新。
缺点:片段标识符中数据长度受限
//父窗口将信息,写入子窗口的片段标识符
var iframeEle = document.getElementById('iframe');
console.log("父页面:" + location.href);
// console.log("父页面获取子页面url:" + iframeEle.contentWindow.location.href);//报错,跨域
/*片段标识符跨域*/
iframeEle.src = iframeEle.src + '#faterData';
window.onhashchange = function(){
console.log('父窗口hash变化:' + window.location.hash);
}
//子窗口监听hashchange事件得到通知
console.log("子页面:" + location.href);
// console.log("子页面获取父页面url:" + top.location.href);//报错,跨域
/*片段标识符跨域*/
window.onhashchange = function(){
console.log('子窗口hash变化:' + window.location.hash);
};
top.location.href = "http://localhost:63342/luna_gather/src/index_test.html#childData3";;
// top.location.href = top.location.href + "#childData2"; ////报错,跨域
3.window.name
浏览器窗口有window.name属性,这个属性的特点是:只要在同一窗口中,无论是否同源,不同网页的window.name属性都是共享的。
优点:window.name数据容量大(相比片段识别符)
缺点:(1)必须同一窗口;(2)必须监听窗口window.name属性的变化,影响网页性能;
4.postMessage
可以实现完全不同源的窗口进行有限的通信。
语法:
(1)发送方发送消息
otherWindow.postMessage(message,targetOrigin,[transfer]);
①otherWindow:其他窗口的引用,如(1)iframeEle.contentWindow;(2)window.open返回的窗口对象;(3)命名过或数值索引的window.frames,如window.frames[0]
②message:传送数据
③targetOrigin:指定接受数据的窗口的源,可以为特定url,或者“*”,表示全部窗口
④transfer:是一串和message同时传递的Transferable对象,这些对象的所有权将被转移给消息的接收方,而发送方将不再保留所有权。
(2)接收方,监听message事件,接收消息
window.addEventListener('message',function(event){});
messageEvent涉及到以下四个属性:
①type:message的类型
②data:postMessage发送过来的数据
③origin:调用postMessage方法的窗口的源,即发送消息的窗口的源
④source:调用postMessage方法的窗口对象
(3)案例演示
父页面:http://a.com/main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<iframe id="iframe" src="http://b.com/iframe.html" frameborder="0"></iframe>
</body>
<script type="text/javascript">
window.onload = function(){
var iframeEle = document.getElementById('iframe');
console.log("父页面:" + location.href);
// console.log("父页面获取子页面url:" + iframeEle.contentWindow.location.href);//报错,跨域
/*postMessage跨域*/
window.frames[0].postMessage("我是父页面", '*');//第一种方式
iframeEle.contentWindow.postMessage("我是父页面2", 'http://b.com/';);//第二种方式
window.addEventListener('message',function(e){
console.log("父窗口接受数据:" + e.data);
});
}
</script>
</html>
子页面:http://b.com/iframe.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<iframe id="iframe" src="http://b.com/iframe.html"; frameborder="0">
</iframe>
</body>
<script type="text/javascript">
window.onload = function(){
console.log("子页面:" + location.href);
// console.log("子页面获取父页面url:" + top.location.href);//报错,跨域
/*postMessage跨域*/
top.postMessage('我是子页面','http://a.com/main.html');
window.addEventListener('message',function(e){
console.log('子窗口接受数据:' + e.data);
e.source.postMessage('我接受到数据了','*');
},false);
}
</script>
</html>
注意:postMessage一定要在页面完成加载之后执行(即onload之后),否则会报错:
Failed to execute ‘postMessage’ on ‘DOMWindow’
可以利用postMessage,实现跨域窗口的localstorage操作。
5、AJAX
同源政策限制,AJAX请求只能发给同源网址,否则跨域报错。
除了架设服务器代理外,(即浏览器请求同源服务器,再由后者请求外部服务器),有三种方法规避同源限制
1、JSONP
2、WebSocket
3、CORS
具体内容,之后单独列出。