微信扫码登录 iframe 方案中的状态拦截陷阱

微信扫码登录 iframe 方案中的状态拦截陷阱

背景

在 Web 端实现微信扫码登录时,常见的方案是使用 iframe 嵌入微信二维码页面。用户扫码授权后,iframe 内部会重定向到我们配置的回调页面,回调页面再通过 postMessage 通知父页面完成登录。

最近在给登录流程增加「用户协议勾选」功能时,遇到了一个有趣的问题:用户勾选协议后扫码,在手机上确认授权前又取消了勾选,结果登录流程依然执行了

问题现象

预期行为:用户取消勾选协议 → 拦截登录流程 → 不跳转

实际行为:用户取消勾选协议 → 控制台显示"未同意协议,不触发事件" → 页面依然跳转了

架构分析

整个微信登录的组件结构如下:

scss 复制代码
Login.vue (页面)
  └── Container.vue
        └── wxQrCodeLogin.vue
              └── iframe (微信二维码)
                    └── WxLogin.vue (回调页面,iframe 内部)

登录流程:

  1. 用户勾选协议 → 显示二维码(iframe)
  2. 用户手机扫码 → 微信授权页面
  3. 用户确认授权 → iframe 重定向到 WxLogin.vue
  4. WxLogin.vue 调用后端接口获取 token
  5. 通过 postMessage 通知父页面
  6. 父页面完成登录跳转

问题根因

wxQrCodeLogin.vue 中,我添加了协议状态拦截:

javascript 复制代码
window.addEventListener("message", (msg) => {
  // 未勾选协议,直接返回
  if(!isAgree.value) {
    console.log("未同意协议,不触发事件");
    return;
  }
​
  if(msg.data.type === '1') {
    emit('qrLoginSuccess', msg.data.token);
  }
});

看起来没问题,但实际上拦截失效了。原因在 WxLogin.vue(iframe 内的回调页面):

javascript 复制代码
if(token) {
    Store.set_cookie('token', token);  // 问题在这里!
    window.parent.postMessage({ type: '1', token }, '*');
}

iframe 内部直接设置了 cookie!

由于 iframe 和父页面同域,cookie 是共享的。当 token 被写入 cookie 后,主站的登录状态检测逻辑检测到 token,自动触发了页面跳转。

整个过程:

  1. 微信授权成功 → iframe 内 WxLogin.vue 执行
  2. Store.set_cookie('token', token)cookie 已写入
  3. postMessage 发送给父页面
  4. 父页面 isAgree 检查 → 返回,不处理
  5. 但 cookie 已经存在 → 主站检测到登录状态 → 跳转

拦截的是 postMessage,但 cookie 的写入发生在 postMessage 之前,根本拦不住。

解决方案

核心原则

iframe 回调页面只负责「中转」,不应该直接操作登录状态(cookie、localStorage 等)。状态的写入应该由父页面根据业务逻辑决定。

代码修改

WxLogin.vue(iframe 回调页面):

javascript 复制代码
// 修改前
if(token) {
    Store.set_cookie('token', token);  // 删除这行
    window.parent.postMessage({ type: '1', token }, '*');
}
​
// 修改后
if(token) {
    // 只传递 token,不设置 cookie
    window.parent.postMessage({ type: '1', token }, '*');
}

父页面在收到 postMessage 后,根据 isAgree 状态决定是否设置 cookie 并完成登录:

javascript 复制代码
window.addEventListener("message", (msg) => {
  if(!isAgree.value) {
    // 可以弹出协议确认弹窗,让用户选择
    return;
  }
​
  if(msg.data.type === '1') {
    // 在这里设置 cookie
    await loginCallback({ token: msg.data.token });
    emit('qrLoginSuccess', msg.data.token);
  }
});

延伸思考

为什么 v-show 不能解决问题?

最初尝试用 v-show 隐藏 iframe,但 v-show 只是 display: none,iframe 依然存在,内部的回调逻辑照常执行。

为什么 v-if 也有问题?

v-if 会销毁 iframe,但如果用户已经扫码进入微信授权页面,此时销毁 iframe 再重建,新的 iframe 无法接收之前扫码的授权回调,用户需要重新扫码。

最佳实践

  1. iframe 回调页面职责单一 :只负责接收授权结果、调用后端接口、通过 postMessage 传递数据
  2. 状态操作由父页面控制:cookie、localStorage、页面跳转等操作都应该在父页面根据业务状态决定
  3. 考虑异步流程中的状态变化:用户可能在异步操作过程中改变状态,设计时要考虑这种边界情况

总结

这个问题的本质是职责划分不清晰导致的。iframe 内的回调页面越权操作了本应由父页面控制的登录状态,使得父页面的拦截逻辑形同虚设。

在设计跨窗口/跨 iframe 通信的功能时,要明确各个组件的职责边界,状态的写入和业务逻辑的执行应该集中在一个地方,避免分散导致的控制失效。

相关推荐
一 乐3 分钟前
校园二手交易|基于springboot + vue校园二手交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端
科技D人生13 分钟前
Vue.js 学习总结(20)—— Vue-Office 实战:word、pdf、excel、ppt 多种文档的在线预览
vue.js·word·vue-pdf·stylesheet·docx-preview·vue-office
vx1_Biye_Design15 分钟前
基于Spring Boot+Vue的学生管理系统设计与实现-计算机毕业设计源码46223
java·vue.js·spring boot·spring·eclipse·tomcat·maven
vx_Biye_Design16 分钟前
基于Spring Boot+vue的湖北旅游景点门票预约平台的设计--毕设附源码29593
java·vue.js·spring boot·spring cloud·servlet·eclipse·课程设计
hedley(●'◡'●)16 分钟前
基于cesium和vue的大疆司空模仿程序
前端·javascript·vue.js·python·typescript·无人机
qq5_81151751518 分钟前
web城乡居民基本医疗信息管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
百思可瑞教育19 分钟前
构建自己的Vue UI组件库:从设计到发布
前端·javascript·vue.js·ui·百思可瑞教育·北京百思教育
百锦再19 分钟前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
hdsoft_huge21 分钟前
1panel面板中部署SpringBoot和Vue前后端分离系统 【图文教程】
vue.js·spring boot·后端
CappuccinoRose1 小时前
JavaScript 学习文档(二)
前端·javascript·学习·数据类型·运算符·箭头函数·变量声明