微信扫码登录 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 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax