同源策略之跨域重识
更新日期:
老调重弹,读书百遍!
这只是是笔记
一. 概念
浏览器的同源策略
同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
1.1 含义关键词
- 域名
- 协议
- 端口
1.2 目的
为了保证用户信息的安全,防止恶意的网站窃取数据。(大家熟悉的 限制跨域 cookie)最初的目的确实是为了限制 跨站访问 cookie
1.3 限制范围
随着互联网的发展,”同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制。
- Cookie、LocalStorage 和 IndexDB 无法读取。
- DOM 无法获得。
- AJAX 请求不能发送。
二. Cookie
Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。
但是两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置 document.domain 共享 Cookie。
比如: http://a.ds.com 和 http://b.ds.com, 那么只要设置相同的 document.domain.两个网页就可以共享 Cookie
但是注意这种方法 只能对 Cookie 和 iframe 窗口的通信有效.
三. iframe 子窗口
如果两个网页不同源,就无法拿到对方的 DOM。典型的例子是 iframe 窗口和 window.open 方法打开的窗口,它们与父窗口无法通信。
如果两个窗口一级域名相同,只是二级域名不同,那么设置 document.domain 属性,就可以规避同源政策,拿到 DOM。
对于完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题。
- 片段标识符(fragment identifier)即 hash
- window.name
- 跨文档通信 API(Cross-document messaging)
3.1 片段标识符
片段标识符(fragment identifier)指的是,URL 的#号后面的部分,比如http://example.com/x.html#fragment的#fragment。如果只是改变片段标识符,页面不会重新刷新。
父窗口可以把信息,写入子窗口的片段标识符。
1 | var src = originURL + '#' + data; |
子窗口通过监听 hashchange 事件得到通知。
1 | window.onhashchange = checkMessage; |
反之亦然。
3.2 window.name
浏览器窗口有 window.name 属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。
父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入 window.name 属性。
1 | window.name = data; |
接着,子窗口跳回一个与主窗口同域的网址,然后主窗口就可以读取子窗口的 window.name 了。
缺点需要 在设置 name 属性之后,更新 url,影响页面性能,操作繁琐。
3.3 window.postMessage
这个方法才是合理的解决方案,以上那些都是钻孔取巧。
HTML5 为了解决这个问题,引入了一个全新的 API:跨文档通信 API(Cross-document messaging)。
这个 API 为 window 对象新增了一个 window.postMessage 方法,允许跨窗口通信,不论这两个窗口是否同源。
举例来说,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。
1 | var popup = window.open('http://bbb.com', 'title'); |
然后监听消息使用 message 事件:
1 | window.addEventListener('message', function(e) { |
3.4 LocalStorage
同理,上面讲过的 postMessage 也可以用来实现 localstorage 传递的桥梁.
比如:
1 | window.onmessage = function(e) { |
四. AJAX
同源政策规定,AJAX 请求只能发给同源的网址,否则就报错。
除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),还有以下方法规避这个限制。
- JSONP
- CORS
4.1 JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个 script 元素,向服务器请求 JSON 数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
首先,网页动态插入 script 元素,由它向跨源网址发出请求。
1 | function addScriptTag(src) { |
服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。
1 | foo({ |
4.2 CORS
CORS 是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,是跨源 AJAX 请求的根本解决方法。相比 JSONP 只能发 GET 请求,CORS 允许任何类型的请求