跨窗口通信
同源策略如何限制窗口之间的访问?
- 动机:
john-smith.com的脚本不能读gmail.com的邮件, 否则用户信息被窃; - 同源定义: 协议、域名、端口三者全部相同;
| URL | 与 http://site.com/ 同源? |
|---|---|
http://site.com/ | 是; |
http://site.com/my/page.html | 是 (路径不影响); |
http://www.site.com | 否 (www. 算另一个域名); |
http://site.org | 否 (域名不同); |
https://site.com | 否 (协议不同); |
http://site.com:8080 | 否 (端口不同); |
- 同源: 对另一个窗口有完全访问权, 变量、
document都能读写; - 异源: 读不到对方任何内容; 唯一例外是
location— 可以写 (等于把对方跳转走), 不能读 (否则泄露用户当前访问的地址); - 前提: 先要有对方窗口的引用, 弹窗靠
window.open/window.opener, iframe 靠iframe.contentWindow/window.frames, 见 弹窗与 iframe; - 结论: 异源时只剩两件事可做 — 写
location和postMessage; - 网络侧: 跨源
fetch的判定与服务端放行头见 跨源请求与 CORS;
postMessage 在跨窗口场景怎么用?
- 定位: 绕过同源策略的唯一正式通道 — 双方都显式调用对应函数才通信, 因此对用户安全;
- 取值:
targetWin.postMessage(data, targetOrigin), 目标窗口引用来自window.opener、window.frames[name]或iframe.contentWindow; targetOrigin的意义: 异源时发送方读不到目标窗口的location, 无法确认它现在打开的是哪个站点 (用户可能已经导航走); 写上期望的源, 消息就只在目标仍在正确站点时送达, 传敏感数据时很重要;
// 目标必须仍是 http://example.com, 否则消息被丢弃
window.frames.example.postMessage("message", "http://example.com");
window.frames.example.postMessage("message", "*"); // 不做站点校验
data: 任意对象, 按结构化序列化克隆; IE 只支持字符串, 复杂对象需先JSON.stringify;- 接收: 目标窗口注册
message事件, 教程指出简写window.onmessage不生效, 要用addEventListener;
window.addEventListener("message", (event) => {
if (event.origin !== "http://javascript.info") return; // 来源校验
console.log("received: " + event.data);
event.source.postMessage("ok", event.origin); // 用 source 直接回信
});
- 事件成员:
data是消息内容,origin是发送方源,source是发送方窗口引用; "*": 等于放弃站点校验, 只在消息完全公开时使用;