使用iframe嵌套页面时用postmessage的交互数据的总结

最近在项目中需要外联一个H5页面,并且app要和这个H5页面进行数据交互,于是就想着总结一下。
其实原本想着在六月份就好好总结一下这个问题,结果都到九月份了,不过经过在两个月中的需求开发中也遇到了关于iframe跨域问题以及父子页面使用PostMessage解决跨域进行数据交互的问题,因此理解更深了。
我们先来看一下,什么是跨域(借鉴了一些别人总结的,因为别人总结的太好了)。

. 为什么会出现跨域问题
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

. 什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

当前页面url 被请求页面url 是否跨域 原因
http://www.test.com/ http://www.test.com/index.html 同源(协议、域名、端口号相同)
http://www.test.com/ https://www.test.com/index.html 跨域 协议不同(http/https)
http://www.test.com/ http://www.baidu.com/ 跨域 主域名不同(test/baidu)
http://www.test.com/ http://blog.test.com/ 跨域 子域名不同(www/blog)
http://www.test.com:8080/ http://www.test.com:7001/ 跨域 端口号不同(8080/7001)

. 非同源限制
【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB

【2】无法接触非同源网页的 DOM

【3】无法向非同源地址发送 AJAX 请求

以上是跨域和跨域的限制问题。

下面我们来看一下使用postmessage怎么解决跨域问题,同时它又是怎么使用的呢?
. postMessage()基本用法

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

otherWindow
其他窗口的一个引用,写的是你要通信的window对象,记住这一点非常重要,这个window对象其实就是你要接收信息的那个窗口,而不是当前页面的window对象
例如在iframe中向父窗口传递数据时,可以写成window.parent.postMessage()window.parent表示父窗口

message
需要传递的数据,字符串或者对象都可以。

targetOrigin
表示目标窗口的源,协议+域名+端口号,如果设置为“*”,则表示可以传递给任意窗口。在发送消息的时候,如果目标窗口的协议、域名或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。例如:

window.parent.postMessage('hello world','http://www.xiaolin.com:8080/index.html')

只有父窗口是http://www.xiaolin.com:8080时才会接受到传递的消息。(协议、域名、端口号三者一致)

[transfer]
可选参数。是一串和message 同时传递的 Transferable 对象,这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。我们一般很少用到。(这个还没用过,以后试一下)

我们看一下怎么接收消息
【接收消息】

window.addEventListener('message', function (e) {
    console.log(e.data)  //e.data为传递过来的数据
    console.log(e.origin)  //e.origin为调用 postMessage 时消息发送方窗口的 origin(域名、协议和端口)
    console.log(e.source)  //e.source为对发送消息的窗口对象的引用,可以使用此来在具有不同origin的两个窗口之间建立双向通信
})

.iframe与父窗口交互数据例子
iframe窗口向父窗口发送消息,记住使用的是 window.parent,因为我们要向window.parent发送消息,window.parent是接收者。(这个例子是向父页面发送了两条信息,只是触发的条件不一样,**那么在父页面接收消息的时候,怎么区分呢?**请接着看)

iframe窗口

document.getElementById("viewer").addEventListener("scroll",(event)=>{
      let [ scrollTop, clientHeight, scrollHeight] = [ event.target.scrollTop, event.target.clientHeight, event.target.scrollHeight];
      scrollNumber++;
      scrollTop + clientHeight + 60 >= scrollHeight ? window.parent.postMessage({toBottom : true},"*") : "";
      scrollNumber == 6 ? window.parent.postMessage({scrolling:true},"*") : "";
});

父窗口(这是用ts语言写的页面,所以获取dom元素时声明一下类型)

window.addEventListener("message",(event)=>{
        if(event.data.scrolling){
          let tipImg = <HTMLElement>document.getElementsByClassName('img')[0];
          img.style.display = "none";
        }
        if(event.data.toBottom){
          let agreeBtn = <HTMLElement>document.getElementsByClassName('query')[0];
          agreeBtn.classList.remove('disabled')
          this.buttonShow = false;
        }
})

在父窗口中我利用event.data中数据不同来判断应该触发对应的事件。当然如果不是同一个子页面发出的,也可以用event.origin来判断。

.安全问题
如果你不希望从其他网站接收message,请不要为message事件添加任何事件监听。
如果你确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份。任何窗口都可以向任何其他窗口发送消息,并且你不能保证未知发件人不会发送恶意消息。而且在验证身份后,你仍然应该验证接收到的消息的语法,防止非法攻击(例如SQL注入)。
使用postMessage将数据发送到其他窗口时,应该指定精确的目标origin,而不是*。恶意网站可以在你不知情的情况下更改窗口的位置,因此它可以拦截使用postMessage发送的数据。

.兼容性
IE6,IE7不支持。
IE8+虽然支持postMessage,但只支持iframe的方式,window.open打开的新窗口之间,没法用。直到IE10才有相关改进。

感谢这两篇博文的帮助:
在iframe中使用postMessage解决跨域问题
什么是跨域?跨域解决方法

发布了130 篇原创文章 · 获赞 103 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/xiaolinlife/article/details/93659889