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>
相关推荐
fury_1233 小时前
vue3:数组的.includes方法怎么使用
前端·javascript·vue.js
weixin_405023373 小时前
包资源管理器NPM 使用
前端·npm·node.js
渗透测试老鸟-九青3 小时前
网络安全之揭秘APT Discord C2 以及如何取证
安全·web安全·网络安全学习路线
宁&沉沦3 小时前
Cursor 科技感的登录页面提示词
前端·javascript·vue.js
隐语SecretFlow3 小时前
【隐语SecretFlow用户案例】亚信科技构建统一隐私计算框架探索实践
科技·算法·安全·隐私计算·隐私求交·开源隐私计算
Dragonir3 小时前
React+Three.js 实现 Apple 2025 热成像 logo
前端·javascript·html·three.js·页面特效
Freshman小白3 小时前
实验室安全准入考试答案
安全·网课答案
荣光波比4 小时前
K8S(十二)—— Kubernetes安全机制深度解析与实践:从认证到RBAC授权
安全·容器·kubernetes