同源策略
早期为了防止 CSRF(跨域请求伪造)的攻击,浏览器引入了同源策略(SOP)来提高安全性。同源策略指的是以下 3 种必须相同
- 协议
- 域名
- 端口
只有满足上面 3 中条件才能产生数据交互,这在一定程序上保证了网站的安全,请看下表
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 成功 | dir2/other.html |
http://store.company.com/dir/inner/another.html | 成功 | dir/inner/another.html |
https://store.company.com/secure.html | 失败 | 不同的协议 ( https ) |
http://store.company.com:81/dir/etc.html | 失败 | 不同的端口 ( 81 ) |
http://news.company.com/dir/other.html | 失败 | 不同的主机 ( news ) |
JSONP 原理
jsonp 利用 script 标签没有跨域的限制,让服务器返回相应的函数可执行字符串,来达到获取不同源服务器的数据
所以可以这样实现
// 声明一个回调函数function jsonp2(data) { alert(data);}var script = document.createElement('script');script.type = 'text/javascript';script.src = 'http://www.a.com?callback=jsonp2';document.getElementsByTagName('head')[0].appendChild(script);
然后服务器根据 callback 参数返回对应的函数执行字符串
// express返回数据res.send('jsonp2({a: 1, b: 2})');
这样一个简单的跨域请求就完成了
缺点:根据 jsonp 的原理,可以发现 jsonp只能发送 get 请求,而不能 post,put,delete 等其他 restful 请求
jQuery 实现
jQuery 实现 jsonp 的例子
$.ajax({ type: 'get', async: false, url: 'http://www.a.com', dataType: 'jsonp', jsonp: 'callback', // 相当于上面的callback,可以自定义 jsonpCallback: 'receive', // 回调函数名,相当于上面的jsonp2,默认jQuery自动生成随机函数名 success: function (data) { alert(data); },});
document.domain
利用 domain 可以获取当前文档的服务器域名,这个属性也可以设置,但是值必须是当前域或其当前域的超级域,例如
www.a.com 可以设置 www.a.com 或者 a.com ,不能设置 b.com, test.a.com
举个栗子
在 http://www.a.com/index.html
的主页面有一个<iframe src="http://news.a.com/news.html"></iframe>
此时想要在 index.html 调用 news.html 的 getNews()方法,只需在两个页面都设置 document.domain = "a.com"
就可以了
如果没设置会报错
Uncaught SecurityError: Blocked a frame with origin "http://localhost:3333" from accessing a frame with origin "http://localhost:3000". Protocols, domains, and ports must match.
设置好 domain 就可以调用 iframe 的方法了
window.frames['aframe'].window.getNews();// 或者document.getElementsByName('aframe')[0].contentWindow.getNews();// 或者document.getElementById('aframe').contentWindow.getNews();
window.name
这个比较少用,自行百度
window.postMessage
利用 html5 的新特性可以解决下面几个方法
1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的 iframe 消息传递
在 iframe 页面监听 message 事件
window.addEventListener('message', receiveMessage, false);function receiveMessage(event) { var origin = event.origin || event.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object. alert(origin);}
然后在当前页面获取对应的 iframe 的调用 window.postMessage()方法,就可以和 iframe 交互,同理 iframe 和父窗体交互,当前页也需要监听 message 事件
window.frames['aframe'].window.postMessage('参数', 'http://localhost:3000');
window.postMessage 语法
otherWindow.postMessage(message, targetOrigin, [transfer]);
message
将要发送到其他 window 的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
targetOrigin
通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个 URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用 postMessage 传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的 orign 属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的 targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
[transfer]可选
是一串和 message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
参考资料:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage