最近两天为实现一个跨域通信的问题,对这个问题进行了相关的学习,心得一二,在此分享。
window.postMessage()
方法
HTML5给我们带来了安全的跨域通信接口,即window.postMessage()方法。它方法原型是:
window.postMessage(msg, domain);
该方法的浏览器支持情况:E8+, FF3+, Chrome, Safari,且在IE下,msg参数必须是string,不能是object.由此可见,我们只要考虑IE6,IE7下的跨域实现。
一、方案一
使用window.postMessage + window.opener实现,该方式是利用IE6,7 opener hack的方式实现跨域,可以算一个安全漏洞。
实现方式:
- 1、www.a.com/a.html 和 www.b.com/b.html,b.html被a.html嵌套在iframe里面。
- 2、首先两个页面各设置目标通信页面的opener = {};opener对象可包含一个方法,这样当前页面可以直接调用目标页面的opener内的方法,并传递参数。
www.a.com/a.html
var otherWindow = document.getElementById("ifr_a").contentWindow; //ifr_a为嵌套b.html的iframe ID
otherWindow.opener={
postMessage:function(str){
alert(str);
}
}
www.b.com/b.html
parent.opener={
postMessage:function(str){
alert(str);
}
}
发送消息:
window.opener.postMessage(message);
这样目标页面就能收到消息了。
我做了一下JS的封装,大家可以直接下载使用:crossMessageEvent_opener.js
下载:crossMessageEvent_opener.js
-
1、a.html和b.html分别引用该JS* 2、分别进行初始化CrossMessageEvent.init(otherWindow); //otherWindow 为目标窗口的window对象* 3、发送消息CrossMessageEvent.postMessage(otherWindow, message, targetOrigin);
otherWindow:为目标窗口的window对象
message:消息内容,可以是object
targetOrigin:目标窗口的域 [可选] -
4、添加消息接收的监听
CrossMessageEvent.receiveMessage(funciton(event){
alert(event.data); //event.data为message的内容
});
该方法的优点:
不需要使用代理页面,简单直接。
该方法的缺点:
只要打过微软的安全补丁.kb2497640就不能用了。
二、方案二
使用Iframe来实现跨域。
理论上使用Iframe的原则:
- 1.当前层级中的任何Window都可以获取其他Window(iframe也是一个Window)* 2.只有同域Window才可以互相操作* 3.当前层级下的任何Window可以设置其他Window的location,即使是不同的域* 4.当你改变url的hashtag(url #后面的字符串)时,页面不会刷新
实现方式:
- 1、www.a.com/a.html 和 www.b.com/b.html 及www.a.com/a_proxy.html 和 www.b.com/b_proxy.html* 2、如图所示
图例中表明了各个页面的iframe嵌套关系。
b.html向a.html发送消息:
- (图中的箭头1)b.html嵌套一个a_proxy.html代理页面,通过将消息值传递到hashtag或iframe的window.name中进行保存* (图中的箭头2)a_proxy.html接收到消息值后,向上找到自己同域的a.html(parent.parent.方法),调用a.html中的一个方法,将消息用参数传递过去。* 理论上来说,由于a_proxy.html和a.html同域,所以a_proxy.html可以操作a.html。在IE6和IE7下测试通过。
a.html向b.html发送消息:
- (图中的箭头3)a.html嵌套一个b.html,并嵌套一个b_proxy.html,将消息能过window.name或hashtag的方法给到b_proxy.html。* (图中的箭头4)b_proxy.html接收到消息后,通过parent.frames['b_ifr'].方法,并将消息通过参数传递过去。* parent.frames['b_ifr']为b.html的iframe的窗口句柄。* 理论上parent.frames['b_ifr']和b_proxy.html是同域下的window互相操作。在IE7下测试通过,有的IE6下也能通过,但是大部份IE6下测试不通过,提示权限不足!
PS:我同事糖饼就是用这个方法实现的:http://www.planeart.cn/?p=1620
三、方案三:crossMessageEvent.js【推荐】
使用window.postMessage + hashtag轮询实现
实现方式:
通过改变目标窗口的location.hash值,目标页面进行轮询监听hash变化,来进行消息传递。
该种方式,不需要使用代理页面,可以避免iframe跨域带来的不稳定因素,由于需要一个定时器,效率稍低,但对于不是频繁的消息传递还是够用的。
crossMessageEvent.js就是采用这种方式来实现ie6,ie7的跨域消息传递。
crossMessageEvent.js使用:
crossMessageEvent.js下载:crossMessageEvent
-
1、a.html和b.html分别引用该JS* 2、发送消息
CrossMessageEvent.postMessage(otherWindow, message, targetOrigin);
otherWindow:为目标窗口的window对象
message:消息内容,可以是object
targetOrigin:目标窗口的域 [可选] -
3、添加消息接收的监听
CrossMessageEvent.receiveMessage(funciton(event){
alert(event.data); //event.data为message的内容
});
该方法的缺点是,会改变URL的hash,导致历史记录发生相应的变化,影响浏览器的前进和后退,且数据暴露在URL。
四、方案四
b页面给a页面发送消息采用方案二中的iframe代理方式。
a页面给b页面发送消息采用hash轮询的方式。
这样可以避免b页面将顶级页面的URL中的hash改变。
———————————————————
参考文献:
http://www.cnblogs.com/xueduanyang/archive/2011/08/23/2150090.html
http://www.alloyteam.com/2012/08/lightweight-solution-for-an-iframe-cross-domain-communication/
http://www.planeart.cn/?p=1620
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。