postMessage 跨域消息传递

743 阅读3分钟

前言

在前端开发中,跨域是一个常见问题,由于同源策略的限制,浏览器不允许在不同源的页面之间直接进行通信。解决跨域问题有很多种方式,window.postMessage() 方法可以安全地实现跨源通信。

postMessage

从广义上讲,一个窗口可以获得对另一个窗口的引用(比如 targetWindow = window.parent),然后在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息。接收消息的窗口可以根据需要自由处理此事件。传递给 window.postMessage() 的参数(比如 message)将通过消息事件对象暴露给接收消息的窗口。

发送消息

调用 postMessage 函数,将消息数据以及目标窗口的源和窗口对象作为参数传递。以下是 postMessage 函数的语法:

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

参数说明:

  • otherWindow :目标窗口的引用,可以是 iframe 或 window 对象。
  • message :要发送的数据。可以是任何可以序列化的 JavaScript 对象。
  • targetOrigin :消息的目标源。只有目标窗口与指定的源相同才会接收到消息。可以是字符串 *,表示任何源都可以接收消息。
  • transfer :要转移的对象,如 Blob 和 ArrayBuffer。

接收消息

要接收 postMessage 发送的消息,需要添加一个事件侦听器来侦听 message 事件。以下是添加事件侦听器:

window.addEventListener('message', function(event) {
   const origin = event.origin;
   // 通常,onmessage()事件处理程序应当首先检测其中的origin属性,忽略来自未知源的消息
   if (origin !== "http://xxxx.xxx:8080") return;
   // ...
}, false)

event 属性有:

  • data :从其他 window 中传递过来的对象。
  • origin : 调用 postMessage 时,消息发送窗口的 origin。这个字符串由 协议://域名:端口号 拼接而成。
  • source : 对发送消息的窗口对象的引用。可以使用此来在具有不同 origin 的两个窗口之间建立双向数据通信。

使用 postMessage 解决跨域问题的基本思路是,在源 A 的页面中嵌入一个 iframe,该 iframe 加载源 B 的页面。当源 A 需要与源 B 互相发送数据时,可以通过 postMessage 方法将数据发送到 iframe,iframe 再将数据发送给接收源。

需要注意的是,使用 postMessage 进行跨域通信时,需要在接收数据的页面中对消息来源进行验证,以避免来自恶意站点的攻击。另外,由于 postMessage 是异步的,不能保证数据的实时性和可靠性,需要谨慎使用。

应用

子页面通过 postMessage 传递 message 到父页面,targetOrigin 为父页面源:

// 子页面
window.parent.postMessage('message', targetOrigin);

父页面通过 window.addEventListener('message',fn) 接收信息,需要判断 origin 是否是来源于子页面:

  // 父页面
  useEffect(() => {
    window.addEventListener('message', messageHandler, false);

    return () => {
      window.removeEventListener('message', messageHandler);
    };
  }, []);

  function messageHandler(e) {
    if (url.includes(e.origin)) {
      console.log(e);
    }
  }

  return (
    <div>
        <iframe
          src={url}
          frameBorder={0}
          width={'100%'}
          loading="lazy"
        />
    </div>
  );

注意事项

除了上述安全性问题,使用 postMessage 时还需要注意以下事项:

  • 不要泄露敏感信息:在发送消息时,不要包含敏感信息,例如密码、用户名等。因为 postMessage 是一种公开的通信方式,可能会被其他网站窃取。
  • 避免滥用:在使用 postMessage 时,需要避免滥用。过多的 postMessage 通信可能会影响网站的性能,并增加安全风险。
  • 跨浏览器兼容性:postMessage 在不同的浏览器中的实现方式可能有所不同。在使用 postMessage 时,需要测试兼容性,并提供替代方案。
  • 避免死循环:在使用 postMessage 时,需要避免死循环。例如,A 网站向 B 网站发送消息,B 网站接收到消息后,又向 A网站发送消息,这可能会导致死循环。
  • 避免被劫持:在使用 postMessage 时,需要防止被点击劫持攻击。点击劫持攻击是指攻击者利用 iframe 或其他技术,将目标网站覆盖在一个透明的 iframe 中,然后诱导用户点击,以达到攻击的目的。为了防止点击劫持攻击,需要在网站中使用 X-Frame-Options 头,以限制网站在 iframe 中的显示。