上面是Wikipedia
对同源策略的定义,简单来说同源策略就是限制不同的源(Domain
)之间的交互的安全策略。想象如果没有同源策略的限制,你访问银行的网站A时的登录信息,在你访问另一个网站B的时候,B网站可以拿到A网站的信息,如果B网站有恶意的代码,就可以直接访问你的银行账户。
同源策略中的同源,是三个相同:
以http://www.example.com
为例:
URL | Outcome | Reason |
---|---|---|
http://www.example1.com | Failure | 域名不同 |
http://www.example.com:8080 | Failure | 端口号不同 |
http://www.example.com/dir/page.html | Same Origin | 仅仅是path不同 |
https://www.example1.com | Failure | 协议不同 |
同源策略对跨源访问的控制可以分为三类;
写操作
是可以,例如表单提交、重定向这些不受同源策略的限制对于开发人员来说,第三条限制,跨域资源之间的读取受到限制,这个是最常遇见的问题,对于第二条中一些HTML原生标签不受跨域访问的限制,可以用来hack
掉同源策略的限制,以下资源是不受同源策略限制的:
<script>标签
外部CSS的加载<link rel="stylesheet" href="...">
<Img>标签
<video>和<audio>标签
<object>、<embed>(<applet>也不受同源策略限制,但该标签已经被废弃)
@font-face引用的字体(浏览器实现有差异,有些要同源)
<iframe>中嵌入的内容
在网页上可以通过设置document.domain
设置当前页面所在的域,例如,在页面http://store.company.com/dir/page.html
中的JS
脚本执行了下面的代码:
document.domain = "company.com";
那么在当前网站上去获取http://www.company.com
的网站上的资源是不受同源策略的限制的,但是这个改变domain
的方法仅仅用于二级域名获取一级域名的资源的时候,你不能在http://www.company.com
的domain
改变为otherCompany.com
。
iframe
在的父子窗口如果不是同源的话,在访问获取彼此的DOM节点或者读取数据的话,会受到同源策略的限制,无法访问。
父窗口所在的域为http://localhost:5500
,其中子窗口所在的域为http://localhost:8080
,这里父子窗口是同源的,所以彼此在获取资源的时候,会受到同源策略的限制:
对于iframe
额跨域问题,有三种解决办法:
fragement
(hash tag)postMessage
这里的fragement
指的是url
中#
后面的部分,在父窗口中把信息作为hash
写入到子窗口的src上。
const originalSrc = document.getElementById('myFrame').src;
const src = originalSrc + '#' + data;
document.getElementById('myFrame').location.replace(src);
因为url hash
的改变不会引起页面的刷新,但是会触发onhashchange
的事件,在子窗口中监听onhashchange
的事件。
function getData() {
const data = window.location.hash;
......
}
window.onhashchange = getData
同样,子窗口也可以通过同样的方式,向父窗口传递数据:
parent.location.replace(parentUrl + "#" + data);
window.name
window.name
在页面进行跳转前设置的值,在跳转后也可以访问到,我们可以借助window.name
的这个特性来解决跨域问题,整个流程如下:
iframe
的src
,在子窗口load
成功后将data
挂到window.name
上,然后设置子窗口的location.href
到与父窗口同域的页面跳转到同域的页面后,父窗口就可以通过window.name
拿到跨域页面设置的数据了
例如:
a.com/index.html // {A}
b.com/index.html // {B}
a.com/empty.html // {C}
我们在A
中加载完成后,在A
页面中设置src
为B
页面的url
,在iframe
加载成功后,将数据设置到window.name
上,然后改变自己的location.href
为C
页面,C
页面加载成功后,A
页面和B
页面就是同源的了,这个时候A
页面去子窗口上取window.name
的值就不会因为跨域问题而报错了。 使用window.name的关键是在设置完window.name后,要将页面跳转到与父窗口同源的页面,这样主窗口才能访问子窗口上设置的数据
html5
提供了postMessage
的机制,用于不同源之间的通信:
在父窗口中监听message
事件
iframe
窗口和父窗口不是同源的,通过postMessage
将消息发送给父窗口。
在浏览器中,script
标签是可以从不同源的地址上加载而不受同源策略的限制,这就给我们提供了一种手段,绕过同源策略的限制,JSONP
就是通过这种手段来达到跨域通信的,但是JSONP
需要服务端的支持,将返回的JSON
数据和url
中的callback
拼接起来,只要前端定义了这个函数,在浏览器看来,就是从服务端加载了一段带数据的函数调用。
CORS(cross-origin resource share)
规范了跨域请求的标准,通过HTTP
头让浏览器允许跨域请求的发送,这个话题,我会在下一篇博客细讲。
兴趣遍地都是,坚持和持之以恒才是稀缺的