需求: 在登录页面通过iframe嵌入钉钉提供的登录页面,用户扫码登录成功后,回到系统首页
问题: 钉钉登录页面授权登录后会携带一个code重定向到预设好的url路径,但是因为它是嵌入在iframe中的,所以重定向后的页面也是嵌入在iframe里面,这显然不是我们期望看到的。
解决思路:
- 钉钉授权登录成功后,重定向到一个中转页面
- 中转页面使用postMessage携带code通知父页面
- 父页面通过window.addEventListener('message')监听消息,并验证消息来源
- 父页面监听到消息,并验证成功后,处理code,将code发送给后台转为token
- 接收到后台发送回的token,登录成功,隐藏iframe并跳转首页
参考文档:
使用钉钉提供的页面登录授权
一文搞懂window.postMessage和window.parent.postMessage
实现代码:
js
// 中转页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>钉钉登录中转</title>
<script>
// 钉钉登录回调页面
window.addEventListener("load", () => {
// 从URL参数中获取授权码
const code = new URLSearchParams(window.location.search).get("code");
if (code) {
// 发送消息给父页面
console.log("发送消息给父页面", code);
window.parent.postMessage(
{
type: "DINGTALK_LOGIN_REDIRECT",
code: code,
},
window.location.origin
);
}
});
</script>
</head>
</html>
js
// 父页面部分html代码
<div class="ding-talk" v-if="useDingtalkLogin">
<el-button class="normal-login" @click="changeLoginType('general')">普通登录</el-button>
<iframe :src="ddLoginUrl" width="100%" height="100%"></iframe>
</div>
<div v-else>
// 普通登录页面
</div>
js
// 父页面逻辑代码【vue2】
mounted() {
this.initMessageListener();
},
beforeDestroy() {
window.removeEventListener("message", this.handleMessage);
},
methods:{
// 初始化消息监听
initMessageListener() {
window.addEventListener("message", this.handleMessage);
},
// 处理接收到的消息
handleMessage(event) {
console.log("event.origin", event.origin, window.location.origin);
if (event.origin !== window.location.origin) {
console.log("忽略来自未知来源的消息:", event.origin);
return;
}
// 验证消息格式
if (event.data && event.data.type === "DINGTALK_LOGIN_REDIRECT") {
console.log("收到钉钉登录重定向消息:", event.data);
this.handleDingtalkLogin(event.data.code);
}
},
handleDingtalkLogin(code) {
// 处理code的逻辑...
this.useDingtalkLogin = false; // 隐藏iframe
},
}