3

最近两天为实现一个跨域通信的问题,对这个问题进行了相关的学习,心得一二,在此分享。

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嵌套关系.png

图例中表明了各个页面的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


via Just Jason's Blog


kidsamong
2.8k 声望138 粉丝

进击的程序猿


引用和评论

0 条评论