iframe实战:跨域通信与安全隔离

iframe

iframe是什么

  • <iframe> 是 HTML 提供的"页中页"标签,它能在当前文档内嵌入一个独立的浏览上下文,拥有自己完整的 window、DOM、CSS 和 JavaScript 运行环境,天然与父页面隔离;借助 postMessage、sandbox、allow 等 API 与安全属性,开发者既可在跨域场景下安全地通信与限制权限,又能通过懒加载、预连接等手段优化性能,因此常被用于第三方内容嵌入、微前端子应用、支付收银台、在线文档预览等需要"隔离 + 互动"的业务场景。

应用场景

  • iframe 把"独立页"嵌入主文档,天然隔离又支持 postMessage 通信,因此常用来加载第三方支付页、微前端子应用、跨域上传组件、在线文档预览、广告或地图等需要安全沙箱、样式隔离、懒加载的模块,既避免脚本冲突,又能随取随用、按需销毁。

通信方式

同源

  • 父页面和 iframe 的源(协议 + 域名 + 端口)完全相同,因此浏览器允许它们直接互相访问对方的 document、window 和 DOM 节点,无需任何消息转发或 postMessage。
    例如下面代码块
js 复制代码
// 父页拿到子页的 document
const childDoc = window.frames[0].document;
childDoc.body.style.background = '#000';   // 直接改样式

// 子页拿到父页的 DOM
window.parent.document.getElementById('header').innerText = 'Hi from iframe';

跨域

  • 使用PostMessage进行跨域通信
  1. 双方先 window.addEventListener('message', handler) 注册监听器;
  2. 发消息用 otherWindow.postMessage(data, origin),data 最好是结构化克隆对象,origin 必须精确匹配,避免 "*";
    例如下列代码块:
js 复制代码
 if (window.parent !== window) {
          // 发送数据到父页面
          window.parent.postMessage({
            type: 'mock_data',
            data: mockData,
            source: 'iframe'
          }, '*'); // 第二个参数是目标origin,'*'表示任何origin
          
          console.log('数据已发送到父页面:', mockData);
          alert('数据已成功发送到父页面!');
        } else {
          alert('当前页面不是iframe,无法发送数据到父页面');
        }

视频演示

  • 下面这两个网页实现了一个iframe父子页面通信的演示系统。子页面(iframe.html)负责生成包含用户名、用户ID、消息内容和时间戳的模拟数据,并通过点击"发送数据到父页面"按钮,使用postMessage API将数据发送给父页面。父页面(parent.html)则通过监听message事件来接收来自子页面的数据,并以美观的卡片形式在页面上方展示接收到的用户信息、消息内容和时间戳,实现了跨域iframe之间的安全数据通信功能。

代码部分

  • 父页面代码
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>iframe父页面</title>
  </head>

  <style>
    #parent {
      background-color: aqua;
    }

    iframe {
      width: 80vw;
      height: 80vh;
    }
  </style>
  <body>
    <div id="parent">
      <h1>这是父页面</h1>
      <div id="received-data">等待接收数据...</div>
      <iframe src="iframe.html" frameborder="0"></iframe>
    </div>
  </body>

  <script>
    // 接收来自iframe的消息
    window.addEventListener("message", function (event) {
      // 验证消息来源(可选,但推荐)
      // if (event.origin !== '期望的origin') return;

      const data = event.data;

      // 检查消息类型
      if (data.type === "mock_data" && data.source === "iframe") {
        console.log("父页面收到数据:", data.data);

        // 显示接收到的数据
        displayReceivedData(data.data);
      }
    });

    // 显示接收到的数据
    function displayReceivedData(mockData) {
      const container = document.getElementById("received-data");

      let html = "<h3>从子页面接收到的Mock数据:</h3>";

      mockData.forEach((item, index) => {
        html += `
            <div class="received-message">
              <strong>消息 ${index + 1}:</strong><br>
              <strong>用户:</strong> ${item.userName} (ID: ${item.userId})<br>
              <strong>消息内容:</strong> ${item.message}<br>
              <strong>时间:</strong> ${item.timestamp}
            </div>
          `;
      });

      html += `<div class="data-info">共收到 ${mockData.length} 条消息</div>`;

      container.innerHTML = html;

      // 滚动到最新数据
      container.scrollTop = container.scrollHeight;
    }
  </script>
</html>

iframe子页面代码

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>iframe子页面</title>
  </head>
  <style>
    #iframe {
      background-color: rgb(255, 204, 0);
      width: 80vw;
      height: 80vh;
    }
  </style>
  <body>
    <div id="iframe">
        <h1>这是子页面</h1>
        <div id="messages-container"></div>
        <button id="send-btn">发送</button>
    </div>
  </body>

  <script>
    // Mock数据生成函数
    function generateMockData() {
      const users = [
        { id: 1001, name: "张三" },
        { id: 1002, name: "李四" },
        { id: 1003, name: "王五" },
        { id: 1004, name: "赵六" },
        { id: 1005, name: "钱七" },
      ];

      const messages = [
        "你好,今天天气不错!",
        "这个功能看起来很棒!",
        "我同意你的观点",
        "有什么新的更新吗?",
        "感谢分享这些信息",
        "期待下次的会议",
        "这个设计很漂亮",
        "代码运行得很顺利",
        "需要帮忙吗?",
        "完成得很棒!",
      ];

      const mockData = [];

      // 生成10条模拟消息
      for (let i = 0; i < 10; i++) {
        const randomUser = users[Math.floor(Math.random() * users.length)];
        const randomMessage =
          messages[Math.floor(Math.random() * messages.length)];

        // 生成随机时间戳(最近7天内)
        const randomDaysAgo = Math.floor(Math.random() * 7);
        const randomHoursAgo = Math.floor(Math.random() * 24);
        const randomMinutesAgo = Math.floor(Math.random() * 60);

        const timestamp = new Date();
        timestamp.setDate(timestamp.getDate() - randomDaysAgo);
        timestamp.setHours(timestamp.getHours() - randomHoursAgo);
        timestamp.setMinutes(timestamp.getMinutes() - randomMinutesAgo);

        mockData.push({
          userId: randomUser.id,
          userName: randomUser.name,
          message: randomMessage,
          timestamp: timestamp.toLocaleString("zh-CN"),
        });
      }

      return mockData;
    }

    // 显示mock数据
    function displayMockData() {
        const container = document.getElementById('messages-container');
        const mockData = generateMockData();

        mockData.forEach(data => {
          const messageDiv = document.createElement('div');
          messageDiv.className = 'message';
          
          messageDiv.innerHTML = `
            <div>
              <span class="user-info">${data.userName} (ID: ${data.userId})</span>
              <span class="timestamp">${data.timestamp}</span>
            </div>
            <div class="message-content">${data.message}</div>
          `;
          
          container.appendChild(messageDiv);
        });
      }

      // 通过postMessage发送数据到父页面
      function sendDataToParent() {
        const mockData = generateMockData();
        
        // 获取当前iframe的window.parent(父窗口)
        if (window.parent !== window) {
          // 发送数据到父页面
          window.parent.postMessage({
            type: 'mock_data',
            data: mockData,
            source: 'iframe'
          }, '*'); // 第二个参数是目标origin,'*'表示任何origin
          
          console.log('数据已发送到父页面:', mockData);
          alert('数据已成功发送到父页面!');
        } else {
          alert('当前页面不是iframe,无法发送数据到父页面');
        }
      }
      // 页面加载完成后显示数据
      document.addEventListener('DOMContentLoaded', displayMockData);

      // 设置发送按钮点击事件
        const sendButton = document.getElementById('send-btn');
        sendButton.addEventListener('click', sendDataToParent);
    </script>
  </script>
</html>
相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
Hello.Reader9 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
智驱力人工智能9 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment10 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
数据与后端架构提升之路10 小时前
论系统安全架构设计及其应用(基于AI大模型项目)
人工智能·安全·系统安全