场景描述
给到我这样的一个需求,在A系统植入B系统,用户不接受打开新窗口再登录B系统的方案,那么只能
采用iframe去做了。
两个系统的token是不一样的,所以要分别在各自的window里存储对应的token。
流程大概如下
A系统点击菜单的时候,去调用B系统登录接口,拿到token之后,先存在A系统window,再将iframe链接到B系统,并且把token传过去,B系统拿到token后,存在自己的window里,再刷新页面,此时B系统已经有了token,B系统页面,自然登录成功。
最后示意图
了解iframe
iframe标签的作用其实就是在指定区域嵌入其他的网页,他本质就是一个打开其他网页的容器。而且它是一个完全独立的宿主环境。
需要注意⚠️的是:
- 嵌入的网页需要与当前网页在统一个域名下,否则会被浏览器视为跨域请求,导致访问受限
- iframe会阻止父级页面的js访问嵌入内容,这是为了防止跨站点脚本攻击,如果需要在父页面中操作嵌入的内容,可以使用postMessage来实现跨窗口通信
更多的属性详见文档。
我们在这里最关键的部分是网页之间的通信,A系统采用contentWindow.postMessage去向B系统发消息,B系统通过监听message事件去检查是否接收到消息。
在MDN上是这么描述postMessage的,window.postMessage() 方法可以安全地实现跨源通信。所以我们可以放心使用。
实现通信过程
A系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import axios from 'axios'; import React, { useState, useEffect, useMemo, useCallback, IframeHTMLAttributes } from 'react';
const baseUrl = "http:localhost:1337"; const systemUrl = "admin/content-manager/collectionType/api::banner.banner?page=1&pageSize=10&sort=title:ASC"; export const IframeDemo: React.FC = () => {
// 发送消息 const sendMessage = useCallback(async() => { const _token = await sessionStorage.getItem("cms_iframe_token"); const _userInfo = await sessionStorage.getItem("cms_iframe_userInfo"); const iframeEl = document.getElementsByTagName("iframe")[0] as HTMLIFrameElement; try { if (_token && _userInfo && iframeEl) { const address = `${baseUrl}/${systemUrl}`; iframeEl.src = address; iframeEl.onload = async function async() { iframeEl.contentWindow && iframeEl.contentWindow.postMessage({ jwtToken: _token, userInfo: _userInfo, url: address, }, address) } } } catch (e) { console.log(e); } }, [])
// 登录 const handleLogin = useCallback(async() => { try { axios.post(`${baseUrl}/admin/login`, { email: "test@163.com", password: "11111111" }).then((res:any) => { const response = res?.data?.data; const _token = `"${response.token}"`; const _userInfo = `${JSON.stringify(response.user)}`; sessionStorage.setItem("cms_iframe_token", _token); sessionStorage.setItem("cms_iframe_userInfo", _userInfo); sendMessage(); }) } catch (e) { console.log(e); } }, [sendMessage])
useEffect(() => { handleLogin(); },[handleLogin])
return useMemo(() => { return ( <div className='h100'> <iframe title="ad" className='cms-iframe' frameBorder={0} id="cms_iframe" width="100%" height="100%" /> </div> ) }, []) }
export default IframeDemo;
|
B系统
1 2 3 4 5 6 7 8 9 10 11 12
| bootstrap(app) { window.addEventListener("message", async(evt) => { if(evt.origin === "A系统url") { const { data: { jwtToken, userInfo, url} } = evt; await sessionStorage.setItem("jwtToken", jwtToken); await sessionStorage.setItem("userInfo", userInfo); try { await window.location.href(url); } catch (e) {} }, false) } }
|
这样就实现了一个基本的通信功能。
我的微信公众号: 梨的前端小屋