Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
T treasure
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 12
    • Issues 12
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Operations
    • Operations
    • Incidents
    • Environments
  • Packages & Registries
    • Packages & Registries
    • Container Registry
  • Analytics
    • Analytics
    • CI/CD
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • External wiki
    • External wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • FE
  • treasure
  • Issues
  • #14

Closed
Open
Created Oct 14, 2020 by liuyajun@liuyajunMaintainer

window.postMessage 在iframe父子页面数据传输,页面之间跨域共享、传输数据,实现单点登录

项目需求

实现单点登录多后台,比如 登录淘宝后,跳转链接到天猫时,天猫处于登录中的状态。

实现方式

基于 Iframe+Postmessage,相互之间发送登录token,实现SSO(SingleSignOn)单点登录功能。

实例

期望结果:假设登录昆仑后台拿到token后,跳转到邻汇吧后台时,页面处于登录状态。

方案一:

  1. 登录昆仑后台拿到token后,跳转到邻汇吧后台时,向邻汇吧内嵌隐藏的昆仑 iframe 发送 getToken 的消息
    iframe.contentWindow.postMessage('getToken', targetOrigin)

  2. 昆仑后台收到消息后(判断 window.parent 是否为邻汇吧后台域名),判断消息是否为 getToken,如果是,发送消息(携带token)给父级 window.parent.postMessage(token, targetOrigin);

  3. 邻汇后台监听 message,收到消息(data.data),判断来源(data.origin)是否为昆仑后台,存储到 localStorage 中
    window.addEventListener('message', data => {}, false);

  4. 以服务的方式分别打开两个html文件,修改各自的 origin、targetOrigin 后,kunlun 页面点击发送按钮,linhui 页面查看是否刷新显示了token

linhui.html

<body>

  <h2>邻汇后台接收token 方案一</h2>

  <div>
    token:
    <span class="token"></span>
  </div>
  <iframe class="iframe" src="" frameborder="0"></iframe>
  <button onclick="getToken()">从iframe拿到token</button>

  <script>
    let origin = 'http://localhost:7788/linhui.html'
    let targetOrigin = 'http://localhost:7788/kunlun.html';
    let iframe = document.querySelector('iframe');
    if (iframe) {
      iframe.src = targetOrigin;
    }

    // 方案一
    function getToken() {
      // 发送消息给 iframe,让他通过 window.parent.postMessage 发送的消息返回token
      iframe.contentWindow.postMessage('getToken', targetOrigin)
    }

    // 监听 iframe 通过 window.parent.postMessage 发送的消息
    window.addEventListener('message', data => {
      let token = data.data
      console.log('message', token);
      localStorage.setItem('token', token);
    }, targetOrigin)


    // 以下只是轮询更新页面上调试用显示的token,和方案无关
    setInterval(() => {
      fn();
    }, 1000);

    function fn() {
      let token = localStorage.getItem('token');
      document.querySelector('.token').innerText = token;
      // console.log('设置token');
    }
  </script>
</body>

kunlun.html

<body>

  <h2>昆仑后台发送token</h2>
  <div>
    token:
    <span class="token"></span>
  </div>

  <script>
    // https://www.cnblogs.com/congxueda/p/11825820.html
    let origin = 'http://localhost:7788/kunlun.html'
    let targetOrigin = 'http://localhost:7788/linhui.html';
    let iframe = document.querySelector('iframe');
    if (iframe) {
      iframe.src = targetOrigin;
    }

    let token = null;

    function createToken() {
      token = Math.floor(Math.random() * 10000000);
      return token;
    }

    // 方案一
    window.addEventListener('message', data => {
      if (data.data === 'getToken') {
        token = createToken();
        window.parent.postMessage(token, targetOrigin);
      }
    }, false);

    
    // 以下只是轮询更新页面上调试用显示的token,和方案无关
    setInterval(() => {
      fn();
    }, 1000);
    function fn(){
      let token = localStorage.getItem('token');
      document.querySelector('.token').innerText = token;
      // console.log('设置token');
    }

  </script>
</body>

方案二(推荐方案二,逻辑简单,子页面只需要接收token即可,昆仑后台内嵌多个项目iframe,统一发送token)

  1. 登录昆仑后台拿到token后,发送消息给内嵌的 邻汇吧后台iframe(邻汇吧后台专门提供一个空白页的路由给iframe使用)
    iframe.contentWindow.postMessage(token, targetOrigin)

  2. 邻汇后台监听 message,收到消息(data.data),判断来源(data.origin)是否为昆仑后台,存储到 localStorage 中
    window.addEventListener('message', token => {}, false);

  3. 以服务的方式分别打开两个html文件,修改各自的 origin、targetOrigin 后,kunlun 页面点击发送按钮,linhui 页面查看是否刷新显示了token

注意

  1. 如果是方案二,就不能使用 sessionStorage,iframe内的 sessionStorage 和 窗口内的 sessionStorage 不会共享
  2. 邻汇吧还要存储版本号,如果不一致会跳出登录,所以要对这部分进行处理

linhui.html

<body>

  <h2>邻汇后台接收token 方案二</h2>

  <div>
    token:
    <span class="token"></span>
  </div>

  <script>
    let origin = 'http://localhost:7788/linhui2.html'
    let targetOrigin = 'http://localhost:7788/kunlun.html';

    // 监听 昆仑后台通过 iframe.contentWindow.postMessage 发送的消息
    window.addEventListener('message', data => {
      let token = data.data
      console.log('message', token);
      // 如果是方案二,就不能使用 sessionStorage,iframe内的 sessionStorage 和 窗口内的 sessionStorage 不会共享
      localStorage.setItem('token', token);
    }, targetOrigin)

    // 以下只是轮询更新页面上调试用显示的token,和方案无关
    setInterval(() => {
      fn();
    }, 1000);

    function fn() {
      let token = localStorage.getItem('token');
      document.querySelector('.token').innerText = token;
      // console.log('设置token');
    }
  </script>
</body>

kunlun.html

<body>

  <h2>昆仑后台发送token</h2>
  <div>
    token:
    <span class="token"></span>
  </div>

  <button onclick="sendToken()">方案二,主动发送token</button>

  <iframe class="iframe" src="" frameborder="0"></iframe>

  <script>
    // https://www.cnblogs.com/congxueda/p/11825820.html
    let origin = 'http://localhost:7788/kunlun2.html'
    let targetOrigin = 'http://localhost:7788/linhui2.html';
    let iframe = document.querySelector('iframe');
    if (iframe) {
      iframe.src = targetOrigin;
    }

    let token = null;

    function createToken() {
      token = Math.floor(Math.random() * 10000000);
      return token;
    }

    // 方案二
    function sendToken() {
      token = createToken();
      iframe.contentWindow.postMessage(token, targetOrigin)
      console.log('sendToken');
    }

    // 以下只是轮询更新页面上调试用显示的token,和方案无关
    setInterval(() => {
      fn();
    }, 1000);

    function fn() {
      let token = localStorage.getItem('token');
      document.querySelector('.token').innerText = token;
      // console.log('设置token');
    }
  </script>
</body>
Assignee
Assign to
None
Milestone
None
Assign milestone
Time tracking