PostMessage

window.postMessage() 方法可以安全地实现跨域的页面通信。

通常情况下,对于两个不同页面的脚步,只有当执行它们的页面位于具有相同的协议(通常是 HTTPS),端口号(443 HTTPS 的默认值),以及主机(两个页面的模数 document.domain 设置为相同的值)时,这两个脚本才能互相通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

语法

targetWindow.postMessage(message, targetOrigin [, transfer])
参数说明
targetWindow通信目标窗口的引用,例如 iframecontentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames
message发送到通信目标窗口的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
targetOrigin通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串或 URI。在发送消息的时候,如果目标窗口的协议主机地址端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送。只有三者完全匹配,消息才会被发送。
transfer(可选)与通讯数据同时传递的 Transferable 对象,这些对象的所有权将被转移给消息的接收方,而发送方将不再保留所有权。

基本用法

发送方

window.postMessage() 调用时,会在所有页面脚本执行完毕后,向目标窗口派发 MessageEvent 消息。

MessageEvent

属性说明
message发送消息的类型
data从其他窗口发送过来的消息对象
origin发送方窗口的源
source发送方的窗口对象
const message = 'Hello world!';
const target = '*';
const transfer = [];
window.postMessage('hello', target, transfer);

源窗口可以是全局 Window 对象,也可以是以下类型的窗口:

  • 文档窗口中的 iframe

    const iframe = document.getElementById('my-iframe');
    const win = iframe.documentWindow;
  • JavaScript 打开的弹窗

    const win = window.open();
  • 当前文档窗口的父窗口

    const win = window.parent;
  • 打开当前文档的窗口

    const win = window.opener();

监听方

一般用于收取发送的消息,message 的属性有。

属性说明
data从源窗口传递过来的对象
origin调用 postMessage 时消息发送方窗口的 origin
source对发送消息的窗口对象的引用
window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) {
// For Chrome, the origin property is in the event.originalEvent
// object
const origin = event.origin || event.originalEvent.origin;
if (origin !== 'http://example.org:8080') {
return;
}
}

安全性

如果您不希望从其他网站接收 message,请不要为 message 事件添加任何事件侦听器。这是一个完全万无一失的方式来避免安全问题。

如果您确实希望从其他网站接收 message,请始终使用 originsource 属性验证发件人的身份。任何窗口都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。但是,验证身份后,您仍然应该始终验证接收到的消息的语法。否则,您信任之发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。

当您使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用 postMessage 发送的数据。


参考资料: