客户端页面的跨域跨文档通信[1]
注意
- 当前标准尚未确定下来,变数很多,该文档也会跟随时间有一定的变动。最后更改时间为:2012-05-08。
- 本文描述的环境为:Chrome 20.0.1123.4 dev & Mac Lion 10.7.3
- Chrome开启选项:
- Threaded compositing 强制开启
- GPU Accelerated SVG and CSS Filters 开启
- Enable Experimental JavaScript 开启
- Enable experimental task manager 开启
问题
主要是为了解决同一浏览器下面,不同域之间的跨文档(页面)交流的问题。官方的设计想法是使用某种方法,可以保证跨文档之间的交流,同时不会导致跨站脚本攻击。跨文档跨域的交流主要是可以让代码复用率增加,同时避免了麻烦的验证关系。
描述下使用场景可能更好理解一些:
当A页面是一个游戏页面,B页面是一个邮件服务商的页面的时候。这个时候当用户在A页面里面点击了增加好友,A页面的脚本通过跨文档通讯方法传递给B消息 [Add someone [email protected]]
,B页面接受到消息时候,可以通过自己的Add方法来将用户的信息增加到通讯录里面。
看起来有点类似 WebService 。不同的是之间的消息传递已从Server端挪动到客户端来通讯了。
window.postMessage
注意
在代码剖析和说明案例之前,应该先明白标题当中的otherWindow
是什么意思。
当调用window.postMessage
的时候,实际上是在触发window
自身的message
事件.整体过程如下:
window.postMessage
=> 触发 window 上的 message
。
也就是说,想要做跨文档通讯的时候,必须先要获取到对方文档的window
,然后使用对方window
的postMessage
方法触发对方的message
事件。所有的循环都在对方的window
里面去走,在这里JS代码只是调用了对方window
上的方法罢了。
我们可以通过var otherWindow = window.open(...)
或者 ` window.frames.contentWindow 来获取
otherWindow。注意尽管
window.open` 从形式上看起来是在不同的窗体之间进行通讯,但是要求这个窗体是通过Parent页面开启,性质上与iframe类似,因此讨论的时候我并没有将其归结于跨窗体通讯(或者伪跨窗体通讯)。
案例
postMessage(cross domain) by html5Demos.com。
代码剖析:
首先,你需要在 HTML 里面放置一个iframe
,src
指定要你想要通讯的页面,没有同域的限制。在该例子里面,demo本身调用的是jsbin.com的内容。
然后,你需要在parent里面添加以下的代码。
最后,在iframe所调用的页面里面写上:
这样代码方面就已经可用了。系统会触发iframe上面 message
事件,并传递进来一个 MessageEvent
Object.内容如下:
说下几个比较关键的参数:
origin
,这个给出了消息来源的域,大部分的安全都是需要使用这个来做判断的。source
,消息来源的window对象,在这个例子上,指的是 Parent 的window对象,我们可以直接使用e.source.postMessage
这个方法来回传信息给 Parent。data
,所有从 Parent 传递过来的数据都在这个属性上面。
优点
- 允许使用iframe进行跨域。
- 使用上很简单,只要搞清楚使用对象即可。
问题:
在 Mozilla 上面提到了这个方法的一些安全问题1:
- 如果你不需要进行通讯,最好不要绑定任何行为在
message
事件上面。 - 一定要验证
origin
和data
里面传递过来的数据。 - 在postMessage的时候,永远不要贪图方便的使用
*
这个来指定接受方的域。 - 这种方法必须要获取到对方的window,当你没有办法获取到
window
的时候(例如你并不是想通过iframe
或者window.open
的方式来打开页面,而是自己在浏览器里面新开一个tab来跑页面),该方法没办法执行。
待测试:
1.iframe 里面是否能获取到e.source 的其他内容?(在chrome 里面使用watch是没办法看到e.source.xxx的值的,然而MDN上却强调了检查source的属性);