参考链接:浏览器同源政策及其规避方法 浏览器的同源策略
W3上是这么解释的: documents retrieved from distinct origins are isolated from each other.直译就是不同源文档的访问是隔离的。
MDN上是这么解释的:同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
对于本地请求
禁止访问(the user agent will disallow access)
对于网络请求
允许发起,不允许接收。
当一个资源请求一个其它域名或者另外一个端口的资源时会产生一个跨域HTTP请求(cross-origin HTTP request)。比如说,http://domaina.example
的某HTML页面通过 的src 请求 http://domainb.foo/image.jpg
。在当今的 Web 开发中,许多页面都会从另外一个站点加载各类资源(包括CSS、图片、JavaScript 脚本以及其它类资源)。
出于安全考虑,浏览器会限制脚本中发起的跨域请求(one origin is permitted to send information to another origin, but one origin is not permitted to receive information from another origin from w3)。比如,使用 XMLHttpRequest 和 Fetch 发起 HTTP 请求就必须遵守同源策略。
跨域并非浏览器限制了发起跨站请求,而是跨站请求可以正常发起,但是返回结果被浏览器拦截了
(1)LocalStorage sessionStorage 和 IndexDB 无法读取。
(2)DOM 无法获得。
(3)AJAX 请求不能发送。
说到这里有些同学可能就会问了,“怎么没有cookie呢?”,解释一下:
1、因为安全机制,在浏览器中js是不能读取本地文件的(后果想想都很严重)
2、我们访问cookie的时候都是通过document.cookie访问的,document是DOM(Document Object Model),文档对象模型的顶层对象
3、同源访问cookie的原理基于当前域下的cookie是由当前域和其父域组成的,即当前域可以访问本身和父域的cookie(默认这样,跟作用域设置有关)
再顺便提一句:
在浏览器中,最顶层的对象为window,其余所有的全局对象、变量和方法如DOM(document)对象,LocalStorage、sessionStorage、indexedDB 等存储对象、screen、location、history等浏览器对象以及Array、Function、Object等js对象都挂载在window上。
同源有三个条件:协议相同、主机名相同、端口相同
举个例子:与http://www.example.com/dir/page.html
相比
规避同源策略我们可以分为以下几种情况来讲:
1、可以访问到其他窗口window对象即iframe或window.opener
2、不同窗口
3、ajax
前两个都是本地的跨域访问,后面一个是对服务器的跨域访问
Javascript的API中,如 iframe.contentWindow, window.parent, window.open
和 window.opener
允许文档间直接相互引用。当两个文档的源不同时,这些引用方式将对 Window
和 Location
对象的访问添加限制。
关于window对象跨域访问属性的表
只有表示顶层窗口的 Window 对象的 operner 属性才有效,表示框架的 Window 对象的 operner 属性无效。
同窗口跨域访问的方法
1、domain
2、片段标识符
3、window.name
举例来说,A网页是http://w1.example.com/a.html
,B网页是http://w2.example.com/b.html
,那么只要设置相同的document.domain="example.com"
,两个网页就可以实现跨域访问。
注意如果两个窗口父级域名相同,只是当前域名不同才能这么设置,否则会出现参数无效的错误。
片段标识符(fragment identifier)指的是,URL的#号后面的部分,
比如http://example.com/x.html#fragment
的#fragment。如果只是改变片段标识符,页面不会重新刷新。
父窗口是可以读写iframe元素的src属性的,因此可以通过片段标识符向子传递信息,同样的,子窗口也可以改变父窗口的片段标识符parent.location.href= target + "#" + hash;
。
父窗口可以获取子窗口的window对象,子窗口在当前域下设置的window.name属性后,再返回父窗口的域(window.name并不会变),父窗口便可以读取子窗口的window.name属性,实现跨域。
通过window.open打开的窗口可以获取父窗口的window对象,因此可以通过iframe一样只是变为父窗口设置window.name,子窗口去获取。
window.postMessage() 方法可以安全的进行跨域通信。通常情况下,不同页面上的脚本,当且仅当这些页面URL的协议、端口、域名相同的时候才可以进行相互访问。 而 window.postMessage() 方法则提供了一种可控机制以便在需要的情况下安全的绕过这些限制。
语法:otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow
目标窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames
message
将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化
targetOrigin(相当于在window的基础上加了一层限制,虽然目标只有一个,但不满足条件也不行)
通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的orign属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点
transfer (可选)
是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权
接收消息
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
// For Chrome, the origin property is in the event.originalEvent
// object.
var origin = event.origin || event.originalEvent.origin;
if (origin !== "http://example.org:8080")
return;
// ...
}
同源政策规定,AJAX请求只能发给同源的网址,否则就报错。
实现ajax跨域的手段有:
1、使用代理服务器
2、JSONP
3、CORS
4、webSocket
由于同源策略是针对浏览器的,因此在服务器端是可以跨域发送ajax请求的,因此可以通过服务器代理的方法实现跨域。
我们知道浏览器自身是可以请求不同域的资源的,不然我们就不可能访问各种网站了。JSONP其实是通过向文档添加 标签,然后由浏览器向目标网址发送http请求来获取资源,而不是由我们的js脚本去发送ajax请求。实际上这已经不是ajax技术了。
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);
};
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
通过在服务器端设置:
Access-Control-Allow-Origin//允许跨域的源
Access-Control-Allow-Methods//允许跨域的请求方式
...
便可以实现跨域。
WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。与http协议是属于同一层的协议。
本质上来说,WebSocket是不限于HTTP协议的,但是由于现存大量的HTTP基础设施,代理,过滤,身份认证等等,WebSocket借用HTTP和HTTPS的端口。
由于使用HTTP的端口,因此TCP连接建立后的握手消息是基于HTTP的,由服务器判断这是一个HTTP协议,还是WebSocket协议。 WebSocket连接除了建立和关闭时的握手,数据传输和HTTP没丁点关系了。
具体参见WebSocket 是什么原理?为什么可以实现持久连接? - 回答作者: Ovear
存储在浏览器中的数据,如localStorage和IndexedDB,以源进行分割。每个源都拥有自己单独的存储空间,一个源中的Javascript脚本不能对属于其它源的数据进行读写操作。
window.name(本身是不能跨域访问的)属性可以用来临时存储数据,可以用于跨域访问。
Cookies使用不同的源定义方式。一个页面可以为本域和任何父域设置cookie,只要是父域不是公共后缀(public suffix)即可。Firefox和Chrome使用Public Suffix List决定一个域是否是一个公共后缀(public suffix)。不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名(sub-domains)来访问cookie。设置cookie时,你可以使用Domain,Path,Secure,和Http-Only标记来限定其访问性。读取cookie时,不会知晓它的出处。尽管使用安全的https连接,任何可见的cookie都是使用不安全的连接设置的。