Skip to content

微信扫码登录是如何实现的?请描述一下技术方案 ​

  • 标签:后端、场景题、系统设计

🏛 业务架构师 ​

微信扫码登录是 OAuth 思想在"App 扫码授权 PC"场景的变体,我按流程 → 状态机 → 安全设计讲,最后点出它为什么是值得投入的功能——扫码登录转化率显著高于账密登录(免输入、免记密码),同时是 App 拉活的重要入口。

完整流程(三端协作:PC 浏览器 / 服务端 / 手机 App)

  1. PC 请求二维码:PC 端调用服务端生成全局唯一二维码 ID(UUID),Redis 存储会话 qrcode:{id},状态"待扫描",TTL 约 2 分钟
  2. PC 轮询/长连接监听:PC 端轮询状态接口(或 WebSocket)等待状态变化
  3. App 扫码:App(已登录态)扫码解析出二维码 ID → 调用"已扫码"接口(带 App 的用户 token)→ 服务端把状态改为"已扫描待确认",返回授权确认页信息(站点名/Logo)
  4. App 确认授权:用户点确认 → 状态变"已确认",服务端生成一次性授权 code 关联该二维码会话
  5. PC 获取登录态:PC 轮询发现"已确认",拿到 code → 换取 PC 端登录态 token(session/JWT)→ 登录完成

状态机设计(核心数据模型)

待扫描 → 已扫描(待确认) → 已确认(登录成功)
   ↘ 过期(超时2分钟, 定时清理)
已扫描 → 用户拒绝 → 终态

状态迁移全部原子操作(Redis Lua 或条件更新),每个状态有明确 TTL。

安全设计(必答)

  • 二维码短时效 + 一次性:2 分钟过期、code 用后即焚——防截屏盗用
  • 确认页防钓鱼:App 确认页必须展示申请登录的站点名称和 Logo——用户扫了钓鱼站点的码能看到异常,最后一道人工防线
  • 扫码即绑定风险提示:异地扫码、频繁扫码触发风控(扫码确认也是账号被盗用的常见通道)
  • 传输安全:二维码 ID 不含敏感信息(只是随机 ID),全链路 HTTPS

收尾:工程上这套"二维码 + 状态机 + 轮询"的模式是通用的——扫码支付、扫码入会、扫码配网全是同一骨架,值得沉淀成通用组件,一次建设多处复用,这才是架构师视角的投入产出。

🏗 系统架构师 ​

微信扫码登录的本质是用手机上的登录态换取 PC 端登录态,核心是 OAuth2 授权码模式 + 扫码确认。我按完整时序讲。

1. 整体角色与流程

text
PC 浏览器           微信服务器(开放平台)        手机微信
   │ 1.请求扫码登录      │                        │
   │──────────────────>│                        │
   │ 2.返回二维码        │                        │
   │  (二维码含唯一       │                        │
   │   uuid/scene_id)   │                        │
   │<──────────────────│                        │
   │ 3.轮询/长连接查状态  │ 4.扫码(手机带微信登录态)  │
   │──────────────────>│<────────────────────────│
   │ "待确认"           │ 5.手机确认授权           │
   │                   │<────────────────────────│
   │ 6.状态变"已确认"    │ 7.生成临时code(换票)      │
   │<──────────────────│───code给手机侧完成闭环────│
   │ 8.PC拿code         │                        │
   │──────────────────>│ 后端用code+appSecret换    │
   │                   │ access_token+openid     │
   │ 9.换取用户信息,建   │                        │
   │   本站会话,登录完成 │                        │

2. 关键技术点

  1. 二维码:内容是一个带 scene_id(全局唯一、5分钟有效、一次性)的 URL,手机微信识别后调开放平台接口"我扫了这个码"——码只是会话媒介,真正的状态都在服务端
  2. 状态查询(PC 侧感知扫码结果):
    • 轮询:每 2s 查一次状态接口(简单,有延迟)
    • 长连接/轮询降级:微信网页版用长连接(服务器 hold 住请求,状态变化立即推送)——扫码体验"秒变"的关键
    • 状态机:等待扫码 → 已扫码待确认 → 已确认(含code) → 过期
  3. 安全设计(重点):
    • code 一次性 + 短时效(5 分钟),换 token 必须带 appSecret(服务端对服务端,浏览器接触不到)
    • 确认动作在手机侧完成(手机已有微信登录态与用户生物/密码验证),PC 浏览器全程拿不到用户凭证,只拿 code
    • 二维码过期机制(防"截图二维码异地扫"的钓鱼:确认页显示"微信网页版登录"目标站点名,用户可识别)
    • redirect_uri 白名单、state 防 CSRF
  4. 本站会话建立:拿到 openid/unionid 后,关联本站账号(已绑定则直接登录;未绑定走绑定/注册流程),签发本站 JWT/session

3. 架构扩展:扫码状态存 Redis(scene_id → 状态,TTL 5 分钟);长连接网关可水平扩展(状态变更经 pub/sub 广播);量级上此模式同样适用于 App 扫码登录自家 PC 站,把"微信"换成"手机 App"即可——面试能点出这个迁移能力就是加分项。

💻 全栈开发专家 ​

微信扫码登录本质是 OAuth2 授权码流程,我按"二维码生成 → 扫码授权 → 换取用户信息 → 建立本地会话"四步讲。

第一步:生成登录二维码

网站后端引导用户跳转微信开放平台授权页,前端展示微信返回的二维码:

text
https://open.weixin.qq.com/connect/qrconnect
  ?appid=APPID
  &redirect_uri=https%3A%2F%2Fmyshop.com%2Fwx%2Fcallback
  &response_type=code
  &scope=snsapi_login
  &state=abc123   # 防 CSRF,会话绑定随机串

二维码状态(待扫码/已扫待确认/已确认/过期)由前端轮询本地接口感知,轮询键是微信侧的 uuid;内部系统自建"App 扫 PC 码"才需要 WebSocket + 二维码 token 状态机。

第二步:用户扫码确认,微信回调

用户微信里点确认后,微信 302 跳回 redirect_uri?code=CODE&state=abc123。后端先验 state(防 CSRF),code 一次性、5 分钟有效。

第三步:后端换取用户信息(服务端完成,不暴露 secret)

java
// ① code 换 access_token + openid
GET https://api.weixin.qq.com/sns/oauth2/access_token
  ?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
// ② 拉 userinfo(scope=snsapi_login 才有昵称头像)
GET https://api.weixin.qq.com/sns/userinfo?access_token=..&openid=..

第四步:关联/注册本地账号,发自己系统的会话

java
User u = userMapper.selectByWxOpenid(openid);
if (u == null) { u = registerByWx(userinfo); }  // openid/unionid 绑定
String token = issueJwt(u.getId());              // 自己签发会话
response.redirect("/login/success?token=" + token);

关键工程点

  • openid vs unionid:同一用户在多个公众号/小程序下 openid 不同,统一账号体系必须用 unionid(开放平台绑定主体后才有)。
  • 首次登录的账号绑定:已注册用户(手机号)首次微信登录,走"绑定"流程而不是新建账号,否则一人两号。
  • 安全:secret 只存服务端;code 换 token 必须服务端执行;回调 URL 白名单;access_token 缓存复用别每次刷新。
  • 降级:微信接口偶发抖动要重试 + 熔断,登录主链路别被第三方拖死。

坑:扫码后长时间不确认 code 过期、二维码本身也要设 2 分钟过期并引导刷新,别让用户对着死码扫半天。