前言
在前端开发中,跨域是一个常见问题,由于同源策略的限制,浏览器不允许在不同源的页面之间直接进行通信。解决跨域问题有很多种方式,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 中的显示。