iframe跨域通信只能用postmessage,因document.domain仅适用于同主域不同子域且已被chrome限制,而targetorigin设为"*"会引发安全风险,必须精确匹配源并校验event.origin、event.source及event.data结构。

iframe跨域通信只能用 postMessage,其他方法要么失效、要么受限极严——这不是权衡,而是浏览器强制执行的安全边界。
为什么 document.domain 在现代项目里基本没用
它只在“同主域、不同子域”时有效,比如 a.example.com 和 b.example.com 可设为 example.com 后互相访问 window。但现实里第三方服务几乎全是独立主域(pay.alipay.com、ads.google.com),根本无法统一 document.domain;而且 Chrome 从 2022 年起已默认禁用该属性对 file:// 协议的支持,本地调试也会失败。
常见错误现象:
- 控制台报错
SecurityError: Failed to set the 'domain' property - 子页面设置了
document.domain = 'example.com',父页也设了,但iframe.contentWindow仍为null
postMessage 的 targetOrigin 绝不能写 "*"
写 "*" 表示允许消息发往任意源,等于主动关闭来源校验。一旦 iframe 被中间人劫持或嵌入恶意页面,父页传的敏感数据(如用户 token、订单 ID)就可能被窃取。
正确做法是精确指定目标源:
- 父页发消息时:用
iframe.contentWindow.postMessage(data, 'https://pay.gateway.io') - 子页收消息时:必须校验
event.origin === 'https://pay.gateway.io',再处理event.data - 若目标源带端口(如
localhost:8080),必须完整匹配,协议、域名、端口缺一不可
iframe 加载完成前调用 postMessage 会静默失败
iframe.contentWindow 在加载完成前是可访问的,但此时调用 postMessage 不会抛错,也不会触发子页的 message 事件——消息直接丢弃。
必须等 load 事件触发后再发:
const iframe = document.getElementById('payment-iframe');
iframe.onload = () => {
iframe.contentWindow.postMessage({ action: 'init', userId: 123 }, 'https://pay.gateway.io');
};
iframe.src = 'https://pay.gateway.io/embed?token=abc';
注意:onload 不代表子页 JS 已执行完毕,如果子页依赖异步初始化(如加载 SDK),需额外约定就绪信号(例如子页初始化完后主动 postMessage({ type: 'ready' }))。
子页监听 message 事件时容易忽略的细节
很多人只写 window.addEventListener('message', handler),但漏掉两件事:
- 没做
event.source校验:攻击者可伪造postMessage,必须确认event.source === iframe.contentWindow(父页场景)或明确信任的窗口引用 - 没清理监听器:单页应用中 iframe 可能被频繁销毁重建,旧的
message监听器若未removeEventListener,会导致内存泄漏和重复响应 - 没验证
event.data结构:直接解构event.data.type可能因undefined报错,应先if (typeof event.data === 'object' && event.data && 'type' in event.data)
跨域通信不是“连上就能聊”,而是每次交互都得重新确认对方身份、消息完整性、上下文有效性——浏览器不帮你记,你得自己盯紧每一步。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











