postMessage解决跨域、跨窗口消息传递

本文详细介绍了浏览器的同源策略及其重要性,以及如何使用window.postMessage进行安全的跨域通信。内容包括消息发送和接收的语法、安全注意事项,并通过实例展示了不同窗口和页面与iframe之间的数据传递。强调了验证消息来源的重要性,以防止潜在的安全风险。
摘要由CSDN通过智能技术生成

同源策略

要理解跨域,我们首先要知道什么是同源策略。

何谓同源:如果两个URL的协议主机地址端口相同,则表示他们同源。

浏览器的同源策略,限制了来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。

  • 根据这个策略,a.com域名下的JavaScript无法跨域操作b.com域名下的对象。比如,baidu.com域名下的页面中包含的JavaScript代码,不能访问google.com域名下的页面内容。

  • 包括Ajax(事实上,Ajax也是由JavaScript组成)。通过XMLHttpRequest对象实现的Ajax请求,不能向不同的域提交,比如,在abc.test.com下的页面,不能向def.test.com提交Ajax请求。

同源策略在现实应用中是十分重要的。假设攻击者利用Iframe把真正的银行登录页面嵌到他的页面上,当用户使用真实的用户名、密码登录时,该策略的存在,就可以避免JavaScript在页面通过读取到用户表单中的内容,泄漏用户名和密码信息。

在浏览器中,<script><link><img><iframe>等标签都可以加载跨域资源,不受同源策略限制,但是通过src加载的资源,浏览器限制了javascript的权限,不能对源文件本身进行读写。

消息传递

window.postMessage() 方法允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本,而不用管是否跨域,可以用这种消息传递技术来实现安全的通信。这项技术称为“跨文档消息传递”,又称为“窗口间消息传递”或者“跨域消息传递”。

postMessage 可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递
  • 页面与嵌套的 iframe 消息传递
  • 多窗口之间消息传递
  • 上面三个问题的跨域数据传递

语法

想要使用 postMessage 实现跨域通信和页面间数据通信,只要记住 window 提供的 postMessage 方法和 message 事件就ok了。

发送消息

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow
其他窗口的一个引用。比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames。

message
要发送的消息。它将会被结构化克隆算法序列化,所以无需自己序列化,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化。

targetOrigin
“目标域“。URI(包括:协议、主机地址、端口号)。若指定为”*“,则表示可以传递给任意窗口,指定为”/“,则表示和当前窗口的同源窗口。当为URI时,如果目标窗口的协议、主机地址或端口号这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会发送。

接收消息

如果指定的源匹配的话,那么当调用 postMessage() 方法的时候,在目标窗口的Window对象上就会触发一个 message 事件。
获取postMessage传来的消息:为页面添加onmessage事件。

window.addEventListener('message',function(e) {
   var origin = event.origin;
   // 通常,onmessage()事件处理程序应当首先检测其中的origin属性,忽略来自未知源的消息
   if (origin !== "http://example.org:8080") return;
   // ...
}, false)

event 的属性有:

  • data: 从其他 window 传递过来的数据副本。
  • origin: 调用 postMessage 时,消息发送窗口的 origin。例如:“http://example.com:8080”。
  • source: 对发送消息的窗口对象的引用。可以使用此来在具有不同 origin 的两个窗口之间建立双向数据通信。

安全问题

  • 如果你不希望从其他网站接收 message,请不要为 message 事件添加任何事件监听器。
  • 如果你确实希望从其他网站接收message,请始终使用 origin 和 source 属性验证发件人的身份。
  • 当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的 targetOrigin,而不是 *

示例

1. 不同 origin 的两个窗口之间建立双向数据通信

/**
* localhost:10002/index页面
**/
// 接收消息
window.addEventListener('message', (e) => {
     console.log(e.data)
})
// 发送消息
const targetWindow = window.open('http://localhost:10001/user');
setTimeout(() => {
     targetWindow.postMessage('来自10002的消息', 'http://localhost:10001')
}, 3000)
/**
* localhost:10001/user页面
**/
window.addEventListener('message', (e) => {
     console.log(e.data)
     if (event.origin !== "http://localhost:10002") return;
     e.source.postMessage('来自10001的消息', e.origin)
     //或者 window.opener.postMessage('来自10001的消息', e.origin)
})

2. 页面与嵌套的 iframe 消息传递
http://www.domain1.com/a.html

<iframe id="iframe" src="http://www.domain2.com/b.html"></iframe>
 
<script>
var iframe = document.getElementById('iframe');
 
iframe.onload = function() {
   // 向domain2发送跨域数据
   iframe.contentWindow.postMessage('来自domain1的消息', 'http://www.domain2.com');
   //或  window.frames[0].postMessage('来自domain1的消息', 'http://www.domain2.com');
};
 
// 接受domain2返回数据
window.addEventListener('message',(e) => {
    console.log(e.data);
}, false);
</script>

http://www.domain2.com/b.html

<script>
// 接收domain1的数据
window.addEventListener('message',(e) => {
    console.log(e.data);
 
    if(e.origin !== 'http://www.domain1.com') return;
 
    // 发送消息给domain1
    window.parent.postMessage('来自domain2的消息', e.origin);
    // 或 window.top.postMessage('来自domain2的消息', e.origin);
    // 或 e.source.postMessage('来自domain2的消息', e.origin);
}, false);
</script>
  • 10
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Web 开发中,当网页中包含来自不同域名的 iframe(内嵌框架)时,由于浏览器的同源策略限制,iframe 之间的直接通信会受到限制。为了实现 iframe 之间的跨域通信,可以使用 postMessage 方法。 postMessage 是一种 HTML5 提供的文档消息传递机制,它允许在不同窗口或 iframe 之间发送消息。通过 postMessage,可以在不同域名之间进行安全的双向通信。 使用 postMessage 进行跨域通信的步骤如下: 1. 在发送消息的页面(发送方)中,使用 JavaScript 调用 postMessage 方法发送消息。该方法接受两个参数:要发送的消息和接收消息的目标窗口的源(origin)。目标窗口的源可以是具体的域名、协议和端口号,或者是通配符 "*" 表示任意源。 ```javascript var targetWindow = document.getElementById('target-frame').contentWindow; targetWindow.postMessage('Hello', 'https://target-domain.com'); ``` 2. 在接收消息的页面(接收方)中,监听 message 事件,通过 event.data 获取接收到的消息。在事件处理程序中可以对消息进行处理。 ```javascript window.addEventListener('message', function(event) { if (event.origin === 'https://source-domain.com') { console.log('Received message: ' + event.data); } }); ``` 通过这种方式,发送方和接收方可以进行跨域通信,并且可以在消息中传递复杂的数据结构。但要注意,为了确保安全性,应该在接收方对来自不同源的消息进行验证,以防止恶意代码的攻击。 需要注意的是,postMessage 方法只能在现代浏览器中使用,兼容性可能会有所差异。此外,在使用 postMessage 进行跨域通信时,也需要确保目标窗口(接收方)支持 postMessage 方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

定栓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值