window.postMessage

2022-08-06,

window.postMessage() 方法可以安全地实现跨源通信(不是浏览器的两个Tab页,而是一个页面中嵌套iframe的跨源通信)。

参考MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

语法
发送: otherWindow.postMessage(message, targetOrigin, [transfer]);
接收:

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://example.org:8080")
    return;

  // ...
}

实战环节

第一步: 环境搭建
1: npm init -y 初始化项目
2: 项目根目录创建文件夹及文件
|- views文件夹
|- father.html
|- child.html
3: 在根目录创建服务应用程序入口:
|- app-postMessage-father.js
|- app-postMessage-child.js

代码如下:
app-postMessage-father.js

/**
 * 一、创建一个最基本的服务
 */
var http = require('http')
const fs = require('fs')
const path = require('path')

http
  .createServer(function (request, response) {
    // 返回html资源
    response.writeHead(200, {
      'Content-Type': 'text/html',
    })

    response.render = function (pathName) {
      fs.readFile(pathName, 'utf-8', (err, buf) => {
        if (err) return
        response.end(buf)
      })
    }

    const url = request.url
    if (url === '/' || url === '/views/father.html') {
      response.render(path.join(__dirname, 'views', 'father.html'))
    }
  })
  .listen(8080)

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8080/')

app-postMessage-child.js

/**
 * 一、创建一个最基本的服务
 */
var http = require('http')
const fs = require('fs')
const path = require('path')

http
  .createServer(function (request, response) {
    // 返回html资源
    response.writeHead(200, {
      'Content-Type': 'text/html',
    })

    response.render = function (pathName) {
      fs.readFile(pathName, 'utf-8', (err, buf) => {
        if (err) return
        response.end(buf)
      })
    }

    const url = request.url
    console.log(url)
    if (url === '/' || url === '/views/child.html') {
      response.render(path.join(__dirname, 'views', 'child.html'))
    }
  })
  .listen(8888)

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/')

**第二步: 采用postMessage实现跨源通信 **
father.html代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>父框架</title>
  </head>
  <body>
    <h1>父框架</h1>
    <!-- src属性指向child资源 -->
    <iframe src="http://127.0.0.1:8888" frameborder="0" id="myIframe"></iframe>

    <script>
      // 3s以后发postMessage通知子框架修改body颜色
      setTimeout(function () {
        const iframe = document.querySelector('#myIframe')
        const iframeWindow = iframe.contentWindow
        // 父窗口给子窗口发送postMessage消息  子窗口监听
        iframeWindow.postMessage('发送给子框架的Message消息', '*')
      }, 3000)

      // 父框架接收子框架发送来的消息
      // window.addEventListener('message', function (event) {
      //   var origin = event.origin || event.originalEvent.origin
      //   console.log('接收到了子框架发来的消息: ' + event.data)
      // })
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>子框架</title>
    <style>
      body {
        background-color: pink;
      }
    </style>
  </head>
  <body>
    <h1>子框架</h1>

    <script>
      // 需求: 当子框架监听到message事件后修改body颜色
      const body = document.querySelector('body')

      // 接收由父框架发送来的message消息
      window.addEventListener('message', function (event) {
        var origin = event.origin || event.originalEvent.origin
        console.log('接收到了父框架发来的消息: ' + event.data)
        // 修改颜色
        body.style.backgroundColor = 'skyblue'
      })

      // 子框架向父框架发送消息
      // window.parent.postMessage('发送给父框架的消息', '*')
    </script>
  </body>
</html>

第三步: 采用node启动两个服务
运行 node app-postMessage-father.js
运行 node app-postMessage-child.js

父页面部署在: http://127.0.0.1:8080/
子页面部署在: http://127.0.0.1:8888/

安全问题
如果您不希望从其他网站接收message,请不要为message事件添加任何事件侦听器。 这是一个完全万无一失的方式来避免安全问题。

如果您确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份。 任何窗口(包括例如http://evil.example.com)都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。 但是,验证身份后,您仍然应该始终验证接收到的消息的语法。 否则,您信任只发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。

当您使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是*。 恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用postMessage发送的数据。

本文地址:https://blog.csdn.net/weixin_44811301/article/details/107303246

《window.postMessage.doc》

下载本文的Word格式文档,以方便收藏与打印。