什么是同源策略
同源策略最早是由Netscape提出的一种安全策略,目前所有支持JavaScript的浏览器都使用这个策略。
同源策略要求:客户端脚本在没有明确授权的情况下,不能读写不同源网址的数据和资源(如HTTP头、Cookie、DOM、localStorage等)。
同源策略只是一个规范,并不是强制要求,各大厂商的浏览器只是针对同源策略的一种实现。它是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
同源是指:
- 域名相同
- 协议相同
- 端口相同
例:ziv.com
协议是 http://
域名是 ziv.com
端口是 80 (默认端口)
以下网址与 ziv.com的同源情况如:
http://www.ziv.com/pc/demo.html 同源
http://ziv.com/h5/demo.html 不同源 域名不同
http://www.ziv.com:81/h5/demo.html 不同源 端口不同
https://www.ziv.com/h5/demo.html 不同源 协议不同
同源策略的意义
当两个网址是非同源时,它们之间的交互会有以下限制:
- Cookie、LocalStorage和IndexDB无法相互读取
- DOM无法相互获取
- AJAX请求不能发送
这些限制都是出于对安全的考虑,都是有必要的。比如,你登录了知乎账号,然后打开了另一个网站,这个网站上的JavaScript可以跨域读取你的知乎账号数据,那这样就毫无隐私可言。又比如,一个恶意网站嵌入了银行账号登录页面,如果没有同源限制,恶意网页的JavaScript就可以获取用户登录的用户信息,想象一下如果是这样后果是不是很严重。
当然,同源策略也会带来一些弊端,导致我们合理的用途也会受到影响。因此,在开发过程中,我们常常需要通过跨域来规避限制。
几种跨域的方法:
![230ee85677a961b55ea1d79d0dbec134.png](https://i-blog.csdnimg.cn/blog_migrate/faeb3cca1bd7b5f7e2c6d0c9423f97e8.png)
document.domain
document.domain 可以用来得到当前网页的域名,同时市场上主流浏览器都支持domain可写,但是只能赋值为当前域名或者基础域名,如果赋值成当前域名的子域名就会报错。
如果两个网页一级域名相同,二级域名不同,则可以通过设置相同的document.domain实现cookie共享和iframe窗口(或window.open打开的窗口)与父窗口之间的通信。
比如,a网页是 http://a.ziv.com/a.html,b网页是http://b.ziv.com/b.html,然后设置它们的document.domain为:
document
那么,a网页设置的cookie,在b网页可以通过js拿到,同样b网页设置的cookie,在a网页也可以通过js拿到。它们的cookie是相互共享的。
如果在a网页通过iframe将b网页设置为a的子窗口:
<iframe id="iframe" src="http://b.ziv.com/b.html"></iframe>
那么,a、b窗口可以相互获得彼此的DOM。
在a网页中
document.getElementById("iframe").contentWindow.document
通过上面的代码可以获取b窗口的DOM。
在b窗口中
window
通过上面的代码可以获取a窗口的DOM。
注意:domain的使用必须在一级域名相同时,才能规避同源限制
window.name
window.name是浏览器窗口的一个属性,在一个窗口的生命周期里,不论是否同源,窗口里载入的所有页面都有权限对window.name进行读写。利用这一特性,我们可以实现在同一个窗口里,载入的不同网页之间的通信。
下面举个实例说明下如何使用window.name进行跨域:
原理:这里用到两个特性,
- 在一个窗口下,先后载入的页面都可以对window.name进行读写
- 同源或者一级域名相同并且domain相同的父子窗口可以相互获得DOM
首先,需要三个页面
- http://a.ziv.com/a.html 获得数据、处理数据的页面
- http://a.ziv.com/proxy.html 中间(代理)页面,一般是没有内容的html文件,必须与a页面一级域名相同,为了使a页面能够获得window.name这个属性
- http://b.viz.com/data.html 拥有数据的页面
具体步骤如下:
- 在数据页面data.html中通过window.name,设置需要的数据。
<script type="text/javascript">
window.name = 'data!'; //需要传输的数据,浏览器一般支持2m的大小,这里数据
//格式可以是json、xml、字符串等类型数据。
</script> - 在处理数据的页面a.html中创建一个iframe,设置其src为数据页面http://b.viz.com/data.html;然后在a.html中监听iframe的onload事件,在事件触发时将iframe的src指向代理页面http://a.ziv.com/proxy.html;这个时候就可以通过iframe的window.name获得数据了。
- 获得数据后要销毁iframe,即释放内存,也保证安全,免得被后续的页面拿到window.name信息。
<script type="text/javascript">
var state = 0,
iframe = document.createElement('iframe'),
loadfn = function() {
if (state === 1) {
var data = iframe.contentWindow.name; // 读取数据
console.log(data); //'data!'
//第三步的代码,销毁iframe,保证安全
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
} else if (state === 0) {
state = 1;
// 设置iframe src为中间页面
iframe.contentWindow.location = "http://a.ziv.com/proxy.html";
}
};
iframe.src = 'http://b.viz.com/data.html';
if (iframe.attachEvent) {
iframe.attachEvent('onload', loadfn); //兼容ie7、ie8
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);
</script>
关于window.name数据格式:
json格式
<script type="text/javascript">
window.name='{"name":"ziv"}';
</script>
json格式应该是程序猿的写法
<html>
<script type="text/javascript">
window.name = document.getElementsByTagName("script") [0].innerHTML.match(/temps*=([wW]*)/)[1];
temp= {
name:"ziv",
age:"24"
}
</script>
</html>
HTML/XML数据格式
<html>
<body>
<p id="data">
this is <strong>html/xml-style</strong> data
</p>
</body>
<script type="text/javascript">
window.name = document.getElementById("data").innerHTML;
</script>
</html>
使用window.name的优缺点
优点
- 安全
- 比使用片段识别符快
- 容量大,可以支持2M左右的数据量
缺点
- 需要监听子窗口window.name属性变化,影响网页性能
window.postMessage
window.postMessage是HTML5的新特性。HTML5支持的跨文档通信API为window对象新增了一个postMessage方法,该方法支持跨窗口通信,不论是否同源。
postMessage使用语法
otherWindow
otherWindow:目标窗口的一个引用(指的是给哪个窗口发消息),比如比如iframe的contentWindow属性,window.open返回的窗口对象。
message:需要发送的数据,通过结构化克隆算法序列化。
targetOrigin:用来指定接收消息的窗口,值可以是字符串“*”或者一个url,“*”表示无限制,url表示接收消息的窗口必须与这个url同源。一般情况下,能确定targetOrigin就不要设置为“*”,免得导致数据泄露。
transfer(可选):一串和message同时传递的transferable对象。传递后,对象的所有权就转移给了消息接收方,发送方不在保留。
接下来我们举个例子具体说明一下,
a网页是 http://aaa.com,b网页是http://bbb.com,在a网页中通过iframe设置b网页为子窗口
<
那么,a、b窗口可以相互获得彼此的DOM。
在a网页中
bWindow
通过上面的代码,a网页可以往b网页发送消息。
b网页往a网页发送消息如下:
window.parent.postMessage('hi~', 'http://aaa.com');
a、b窗口需要通过监听message事件得到彼此发送的消息。
window
message的事件对象有三个属性:
event
b窗口可以通过message事件对象的属性source来指定回复消息的url,a、b可以通过origin属性过滤不是给自己发送的消息。
window
片段识别符
片段识别符是url #号后面的部分,比如:
http://www.ziv.com/a.html#hello
#后面的hello就是片段识别符,也称为hash
片段识别符有几个特点:
- 修改片段识别符,页面不会重新刷新
- 父窗口可以改变子窗口的片段识别符
- 子窗口可以改变父窗口的片段识别符
- 通过hashchange事件可以监听片段识别符的变化
根据片段识别符的特点,我们可以完成父子窗口通信