平时我们在进行web开发时,总会遇到一些数据的传递,其中最常见的当属前后端数据的传递。当然,除了前后端数据传递,还有一些其他的场景,比如:

  1. 页面和其新打开的窗口之间的数据传递
  2. 多窗口之间的数据传递
  3. 页面与内嵌的iframe数据传递

这些都涉及到跨域。那么什么是跨域?

什么是跨域

在HTML中,<a>, <form>, <img>, <script>, <iframe>, <link>等标签以及ajax都可以指向一个资源地址,而所谓的跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一样,那么请求就会失败。这里的域指的是:若是协议+域名+端口号均相同,那么就表明是同域。跨域最根本的原因还是浏览器的同源策略。
下表给出了https://www.shchome.top 同源示例:

URL结果原因
http://www.shchome.top失败协议不同
https://www.shcblog.com失败域名不同
https://www.shchome.top:8080失败端口不同
https://www.shchome.top/page/2/成功协议,域名,端口均相同

那么浏览器为什么要使用同源策略这种机制呢?
浏览器引入同源策略主要是出于安全考虑,防御CSRF攻击。CSRF主要是利用用户的登录状态发起恶意请求。
试想一下,如果没有同源策略,那么我们每次发起HTTP请求都会带上请求地址的cookie,此时黑客就可以做如下攻击:

  1. 用户登录了自己的网上银行页面 mybank.com, mybank.com向用户的cookie中添加用户的标识。
  2. 用户浏览了恶意页面 virulence.com,执行了页面中恶意的JS代码。
  3. virulence.com向mybank.com发起恶意的HTTP请求,请求会默认把mybank.com对应的cookie也同时发送过去。
  4. mybank.com服务端从发送的cookie中提取用户标识,验证用户无误,response中返回请求的数据,此时数据就泄露了。

iframe之间的跨域也是类似,如果iframe之间可以跨域访问,那么可以这样攻击:

  1. 仿造一个假网站,里面用iframe嵌套 mybank.com 银行网站。
  2. 设置iframe样式,使得网站和银行的网站之间除了域名之外没有任何差别。
  3. 这时如果用户输入用户名密码,这样主网站就可以通过跨域访问mybank.com整个DOM节点,这样就可以拿到用户的输入。

所以说有了跨域限制之后,我们就能更安全的上网了。当然,跨域并不能完全的防止CSRF攻击。

现在我们再来考虑一个问题,请求跨域了,那么请求到底有没有发出去?
请求必然是发出去了,但是在请求响应时,被浏览器拦截了。跨域是为了阻止用户读取到其他域名下的内容,ajax可以获取响应,但是浏览器认为这是不安全的,所以拦截了响应。这也可以说明跨域并不能防止CSRF攻击,因为请求毕竟是发出去了。

今天我们先来说说解决跨域的一种方法–postMessage。

什么是postMessage

postMessage 是HTML5引入的一个新的API,postMessage方法允许来自不同源的脚本采用异步方式进行通信。可以实现跨文本文档,多窗口,跨域消息传递,多用于窗口间数据通信,这也使它成为跨域通信的一种有效解决方案。

postMessage的使用

发送消息

1
otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow

窗口的一个引用,比如iframe的contentWindow属性,执行window.open返回的窗口对象,或者是命名过的或数值索引的window.frames。

message

需要发送到其他窗口的数据,注意:message在发送时会被序列化。

targetOrigin

通过窗口的origin属性来指定哪些窗口能够接收到消息事件,指定后只有对应origin的窗口才能接收到消息。设置通配符‘*’表示可以发送到任意的窗口,但是出于安全性最好不要这么做,如果想要发送到和当前窗口同源的窗口,只需要设置为‘/’即可。

transfer | 可选属性

是一串和message同时传递的Transferable对象,这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

接收消息

目标窗口是通过执行方法监听函数来接收发送过来的消息

1
2
3
4
5
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event){
if (event.origin !== "https://www.shchome.top")
return;
}

event对象有三个属性,分别是origin,data和source。event.data表示接收的消息,event.origin表示发送message的消息源,包括源的协议、url、端口;event.source表示发送消息的窗口的引用。通过这个引用我们可以建立两个源之间的通信。

postMessage解决跨域问题

postMessage解决跨域非常简单,在源消息页面只需要使用如下代码

1
window.parent.postMessage('isClosed', 'https://shchome.top')

在接收消息的页面中

1
window.addEventListener('message', msgHandler, false)

本文简单的介绍了跨域和解决跨域方法的一种,跨域作为前端一个比较重要的知识点,掌握跨域还是很有必要的。而postMessage作为HTML5中一个新的API,能够很好的解决跨域问题,特别是不同窗口之间的通信,因此了解它也是有必要的。