window.postMessage()
方法可以安全地实现跨域的页面通信。
通常情况下,对于两个不同页面的脚步,只有当执行它们的页面位于具有相同的协议(通常是 HTTPS),端口号(443 HTTPS 的默认值),以及主机(两个页面的模数 document.domain
设置为相同的值)时,这两个脚本才能互相通信。window.postMessage()
方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
targetWindow.postMessage(message, targetOrigin [, transfer])
参数 | 说明 |
---|---|
targetWindow | 通信目标窗口的引用,例如 iframe 的 contentWindow 属性、执行 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// objectconst origin = event.origin || event.originalEvent.origin;if (origin !== 'http://example.org:8080') {return;}}
如果您不希望从其他网站接收 message,请不要为 message 事件添加任何事件侦听器。这是一个完全万无一失的方式来避免安全问题。
如果您确实希望从其他网站接收 message,请始终使用 origin
和 source
属性验证发件人的身份。任何窗口都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。但是,验证身份后,您仍然应该始终验证接收到的消息的语法。否则,您信任之发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。
当您使用 postMessage
将数据发送到其他窗口时,始终指定精确的目标 origin
,而不是 *
。恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用 postMessage
发送的数据。
参考资料: