专栏首页前端自习课【JS】504- HTML5 之跨域通讯(postMessage)

【JS】504- HTML5 之跨域通讯(postMessage)

本文来自公众号【前端早读课】,最近工作中用在使用 postMessage ,所以一起分享给大家一下~


前言

大家跨域的文章估计也看了很多了。这次应用到一个场景中了解了一下。今日早读文章由高途课堂@王嘉涛授权分享。

王嘉涛,英文名:Jartto,高途课堂前端技术专家,擅长 Web 领域技术,对性能优化有较深研究。目前正在从事 Web 前端与 AI 方向的研究。喜欢读书,写博客以及网球运动。

正文从这开始~~

场景:统一登录这个应用场景,相信大家都不陌生。如果公司内有好几个域名想共用一个统一登录,除了中转页跳转外,还有一种就是当前页弹窗。如何不同域传登录数据就是本文可以了解到的了。

很多情况下,我们受到浏览器的安全策略限制。如何能规避此限制,并且能安全的使用跨域通讯,这就不得不介绍一下 postMessage 了。

一、关于 postMessage

window.postMessage() 方法可以安全地实现跨源通信。

通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为 https),端口号(443 为 https 的默认值),以及主机 (两个页面的模数 Document.domain 设置为相同的值) 时,这两个脚本才能相互通信。

window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

二、理解过程

window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后向目标窗口派发一个 MessageEvent 消息。该 MessageEvent 消息有四个属性:

1.message 属性表示该 message 的类型;2.data 属性为 window.postMessage 的第一个参数;3.origin 属性表示调用 window.postMessage() 方法时调用页面的当前状态;4.source 属性记录调用 window.postMessage() 方法的窗口信息。

关于更多细节,我们可以查看 MDN 文档,这里就不赘述了。

三、兼容性

caniuse

通过上面的图片,我们可以看出来,几乎所有的浏览器都支持了 postMessage,所以放心大胆的去使用吧。

四、用法简介

基本用例:

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

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

message

将要发送到其他 window 的数据。它将会被结构化克隆算法序列化。

这意味着我们可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。

targetOrigin

通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串 「*」(表示无限制)或者一个 URI。

在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;

只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口。

我们举个例子,当用 postMessage 传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的 origin 属性完全一致,来防止密码被恶意的第三方截获。

如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的 targetOrigin,而不是 *。

需要注意:不提供确切的目标将导致数据泄露到恶意站点。

transfer

是一串和 message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

五、事件监听

我们来看下面这段代码:

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
// For Chrome, the origin property is in the event.originalEvent
// object.
// 这里不准确,chrome没有这个属性
// var origin = event.origin || event.originalEvent.origin;
var origin = event.origin
if(origin !== "http://jartto.wang:8080")
return;
// ...
}
data

从其他 window 中传递过来的对象。

origin

调用 postMessage 时消息发送方窗口的 origin . 这个字符串由 协议、://、域名、: 端口号 拼接而成。

例如 https://jartto.wang (隐含端口 443)、http://jartto.net(隐含端口 80)、http://jartto.com:8080。请注意,这个 origin 不能保证是该窗口的当前或未来 origin ,因为postMessage 被调用后可能被导航到不同的位置。

source

对发送消息的窗口对象的引用, 我们可以使用此来在具有不同 origin 的两个窗口之间建立双向通信。

六、简单应用

监听 message 事件:
window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  console.log('get it !!!',event);
}
发送数据
window.postMessage({
    name: 'Jartto',
    say: 'hello~',
    arr: [1,2,3]
}, '*');

例子

发送方
<div>
<inputid="text"type="text"value="前端早读课"/>
<buttonid="send">发送消息</button>
</div>

<script>
    window.onload = function() {
// var receiver = window.parent;
var receiver = window.opener;
var btn = document.getElementById('send');
        btn.addEventListener('click', function(e) {
            e.preventDefault();
var val = document.getElementById('text').value;
            receiver.postMessage("Hello "+val+"!", "http://localhost:63342");
});
}
</script>
接收方
<buttonid="j-open">打开页面</button>
<divid="message">
    Hello World!
</div>
<script>
    window.onload = function() {
        document.getElementById('j-open').onclick = function(){
            window.open('http://localhost:8880/1/send.html','sendFrame', 'height=100, width=400, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=n o, status=no');
};
var messageEle = document.getElementById('message');
        window.addEventListener('message', function(e) {
if(e.origin !== "http://localhost:8880") {
return;
}
            messageEle.innerHTML = "从"+ e.origin +"收到消息:"+ e.data;
});
}
</script>

关于本文作者:@jartto原文:http://jartto.wang/2019/06/11/post-message/

本文分享自微信公众号 - 前端自习课(FE-study),作者:王嘉涛

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【Web技术】441- 蚂蚁前端研发最佳实践

    准备这个题目时我 google 了下前端最佳实践,排在前面的是讲前端代码规范,语意、可读性、编码规范、空格还是 Tab 等等,我觉得这是我们第一代的最佳实践。

    pingan8787
  • 【JS】246-如何在JavaScript面试中过五关斩六将?

    JavaScript 面试不容易。我觉得难,你也觉得不容易,大家的意见不谋而合。在 JavaScript 面试中被问问题的概率通常很高。那么该如何破解 JS 面...

    pingan8787
  • 【Taro】363- 玩转 Taro 跨端之 flex 布局篇

    Taro 是一套遵循 React 语法规范的跨平台开发解决方案,但是目前当我们使用 Taro 的时候,在不同平台上的开发体验还有不一致的地方,所以我们也都期待有...

    pingan8787
  • 干货 | 证件全文本OCR技术,了解一下

    携程技术
  • Knockout.Js官网学习(visible绑定)

    让visible绑定到DOM元素上,使得该元素的hidden或visible取决于绑定的值。

    aehyok
  • 【通信】跨文档通信含代码说明

    出于安全和隐私方面的考虑,在web浏览器中,实施了不同域名下的文档间不能通信的举措,也就日常说的禁止跨域执行脚本。

    Html5知典
  • EUREKA生产环境的参数如何优化?

    之前微服务项目的时候一直没有时间去总结,最近闲下来了,可以好好的把以前学习的微服务的知识和微服务的项目好好总结归纳一下了。

    技术从心
  • 开发自己的Data Access Application Block[下篇]

    上接:[原创] 我的ORM: 开发自己的Data Access Application Block - Part I 4. Database 下面来介绍重中之重...

    蒋金楠
  • .NET Remoting 体系结构 之 生命周期管理

    对于客户端,答案比较简单。只要客户端调用远程对象上的方法,就会产生一个 System.Runtime.Remoting.RemotingException 类型...

    DougWang
  • tensorflow: tensorboard 探究

      代码运行完成之后,可以用bash脚本一键浏览器访问tensorboard终端:

    JNingWei

扫码关注云+社区

领取腾讯云代金券