iframe跨域通信方法(转自http://www.2cto.com/kf/201211/166555.html)

来源:互联网 发布:网络支付安全问题 编辑:程序博客网 时间:2024/04/30 13:06

对于双向跨域通信来说,选择的方案也不是很多,综合各种因素,我们选择FIM + window.postMessage的组合方式来解决跨域通信问题。因为这两种方式各有优缺点,window.postMessage,使用非常简单,但是由于是个比较新的方法,IE6和IE7等老式浏览器不支持。FIM方法支持所有的浏览器,但比较繁琐,而且容易产生浏览记录,因此如果浏览器支持window.postMessage就采用window.postMessage,否则就采用FIM技术。

window.postMessage解决方案
window.postMessage是HTML5定义的一个很新的方法,这个方法可以很方便地跨window通信。由于它是一个很新的方法,所以在很旧和比较旧的浏览器中都无法使用,比如IE6和IE7,IE8已经支持这个方法了。

window.postMessage的使用方法比较简单,只有两个参数,第一个参数是要传输的消息,第二个参数是接收消息的域,可以用“×”来表示所有的域。发送消息的代码如下:

  var o = document.getElementsByTagName('iframe')[0];
  o.contentWindow.postMessage('Hello world', 'http://b.example.org/');
接收消息页面的代码如下:

  window.addEventListener('message', receiver, false);
  function receiver(e) {
    if (e.origin == 'http://example.com') {
      if (e.data == 'Hello world') {
        e.source.postMessage('Hello', e.origin);
      } else {
        alert(e.data);
      }
    }
  }
注意这里的绑定事件方式只针对非IE浏览器,IE浏览器需要用attachEvent方式来绑定事件。在上面的代码里你可以决定是否要判断消息的来源,这里是从安全角度考虑,防止不安全的消息。

FIM (Fragment Identitier Messaging)
FIM (Fragment Identitier Messaging)的原理是基于父窗口可以对iframe进行URL读写,iframe也可以写父窗口的URL(注意,跨域时iframe是不可以读取父窗口的URL的,但可以修改父窗口的URL),URL有一部分被称 为frag,就是#号及其后面的字符,它一般用于浏览器锚点定位,Server端并不关心这部分,应该说HTTP请求过程中不会携带frag,所以这部分的修改不会产生HTTP请求,也就是页面不会刷新,但是会产生浏览器历史记录。FIM的原理就是改变URL的frag部分来进行双向通信。每个window通过改变其它window的location来发送消息,并通过监听自己的URL的变化来接收消息。这个方式的通信会造成一些不必要的浏览器历史记录,而且有些浏览器 不支持onhashchange事件,需要轮询来获知URL的改变,最后,URL在浏览器下有长度限制,这个制约了每次传送的数据量。

先说父窗口向iframe发送消息,代码如下:
  var o = document.getElementById('iframe');
  o.href = 'http://www.weakweb.com#name=boris';
这样我们就把值传到iframe里面了,下面就需要考虑iframe如何监听何时有消息传递过来。这里有两个方法:1,在iframe里使用setInterval()轮询来判断当前的iframe的URL是否发生了变化,缺点是轮询会产生性能消耗;2,在父窗口里改变iframe的大小来触发iframe的window.onresize事件,缺点是要改变iframe窗口的大小。当iframe发现有消息传送过来时就可以通过locaiton.hash来读取了。

iframe向父窗口发送消息
  parent.location.href = 'http://www.company.com#height=100';
上面提过,跨域时iframe可以写父窗口的URL,但是不可以读取的,所以这里我们就通过frag的方式把消息传递到父窗口了,同样,父窗口也需要监听啥时候有消息传进来,这里只能用轮询的方式了。

对FIM的一点改进
当父窗口采用修改iframe窗口大小的方式告诉iframe有消息传递方式时,这种方式有一个非常明显的缺点,那就是iframe窗口的大小改变会严重影响用户体验。因此这里采用的代理机制来处理。

我们额外创建了代理iframe叫proxyIframe吧,proxyIframe和之前的contentIframe是同一个域的,同时proxyIframe是隐藏不可见的,这样我们先把消息传递到proxyIframe里,然后在由proxyIframe发送到contentIframe,因为这两个iframe是同域的,所以可以很方便的传递。这样做的好处是我们只修改proxyIframe的大小而不用修改contentIframe的大小,这样不会影响用户体验,缺点是比之前复杂了一些。

浏览器的URL长度是有限制的,尤其是IE,所以我们可以通过分段传输来解决这个问题。

示例代码
我写了一个测试代码,其中包含了三个页面,一个父页面和两个iframe页面,点击下载

一个如何在本地测试运行上面的示例代码的简单方法,在C:\Windows\System32\drivers\etc\hosts文件,在里面添加两个域名指向127.0.0.1,这样就可以测试跨域了。我的hosts文件如下:

   127.0.0.1       a.com
    127.0.0.1       b.com

0 0