iframe的一次应用


场景描述

给到我这样的一个需求,在A系统植入B系统,用户不接受打开新窗口再登录B系统的方案,那么只能
采用iframe去做了。

两个系统的token是不一样的,所以要分别在各自的window里存储对应的token。

流程大概如下

A系统点击菜单的时候,去调用B系统登录接口,拿到token之后,先存在A系统window,再将iframe链接到B系统,并且把token传过去,B系统拿到token后,存在自己的window里,再刷新页面,此时B系统已经有了token,B系统页面,自然登录成功。

最后示意图

了解iframe

iframe标签的作用其实就是在指定区域嵌入其他的网页,他本质就是一个打开其他网页的容器。而且它是一个完全独立的宿主环境。

需要注意⚠️的是:

  1. 嵌入的网页需要与当前网页在统一个域名下,否则会被浏览器视为跨域请求,导致访问受限
  2. 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)
}
}

这样就实现了一个基本的通信功能。

我的微信公众号: 梨的前端小屋


文章作者: 梨啊梨
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 梨啊梨 !
  目录