如何实现两个窗体间安全地跨源通信?
window.postMessage(message, targetOrigin, [transfer]) - 发送消息 - 通过异步消息传递的方式在来自不同源的脚本之间传递消息。
window.addEventListener("message", receiveMessage, false) - 接受消息 - 如果指定的源匹配的话,那么当调用 postMessage() 方法的时候,在目标窗口的Window对象上就会触发一个 message 事件
应用于多个场景:
- 页面与其新打开的窗口之间的数据传递
- 页面与嵌套的iframe之间的数据传递
- 多窗口之间的数据传递
如何使用?
发送信息使用:otherWindow.postMessage(message, targetOrigin, [transfer]);
参数解析:
otherWindow:其他窗口的一个引用。比如targetWindow = window.opener,然后在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息
message:将要发送到其他窗口的数据,它将会被结构化克隆算法序列化,所以无需自己序列化(部分低版本浏览器只支持字符串,所以发送的数据最好用JSON.stringify() 序列化)
targetOrigin:用来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个 具体的URI(推荐使用,安全性更高)。这个机制用来控制消息可以发送到哪些窗口,来防止密码被恶意的第三方截获
transfer(可选):是一串和 message 同时传递的 [Transferable] 对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权
接收信息使用:window.addEventListener("message", receiveMessage, false);
监听message事件
message的属性有哪些:
data:从其他 window 中传递过来的对象。origin:调用 postMessage 时,消息发送窗口的 origin。例如:“example.com:8080”。souurce:对发送消息的窗口对象的引用。可以使用此来在具有不同 origin 的两个窗口之间建立双向数据通信。
实列:(A,B窗口互发消息)
A 窗口的域名是example.com:8080,B窗口域名是example.org
A窗口:
var popup = window.open(...popup details...);
// 这行语句没有发送信息出去,即使假设当前页面没有改变 location(因为 targetOrigin 设置不对)
popup.postMessage("The user is 'bob' and the password is 'secret'",
"https://secure.example.net");
// 假设当前页面没有改变 location,这条语句会成功添加 message 到发送队列中去(targetOrigin 设置对了)
popup.postMessage("hello there!", "http://example.org");
function receiveMessage(event)
{
// 我们能相信信息的发送者吗?(也许这个发送者和我们最初打开的不是同一个页面).
if (event.origin !== "http://example.org")
return;
// event.source 是我们通过 window.open 打开的弹出页面 popup
// event.data 是 popup 发送给当前页面的消息 "hi there yourself! the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
B窗口:
//当 A 页面 postMessage 被调用后,这个 function 被 addEventListener 调用
function receiveMessage(event)
{
// 我们能信任信息来源吗?
if (event.origin !== "http://example.com:8080")
return;
// event.source 就当前弹出页的来源页面
// event.data 是 "hello there!"
// 假设你已经验证了所受到信息的 origin (任何时候你都应该这样做), 一个很方便的方式就是把 event.source
// 作为回信的对象,并且把 event.origin 作为 targetOrigin
event.source.postMessage("hi there yourself! the secret response " +
"is: rheeeeet!",
event.origin);
}
window.addEventListener("message", receiveMessage, false);
安全问题
- 如果你不希望从其他网站接收 message,不要为 message 事件添加任何事件侦听器。
- 如果确实希望从其他网站接收 message,始终使用 origin 和 source 属性验证发件人的身份。
- 当使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是*