PC 端扫码登录的完整交互流程和技术实现是怎样的?
PC 端扫码登录是目前非常普遍的登录方式(如微信、淘宝、Telegram 等)。它的核心原理在于:通过手机端已有的登录凭证(Session/Token),授权 PC 端获取登录凭证,从而完成登录。
这个过程本质上是一个将“临时 ID”与“用户身份”进行绑定的过程。
以下是完整的交互流程和技术实现详解。
一、 核心角色
- PC 端(Client A): 浏览器或桌面客户端,处于未登录状态,需要登录。
- 手机端(Client B): App,处于已登录状态,拥有用户的 Auth Token。
- 服务端(Server): 处理认证、生成二维码 ID、维护状态。
二、 完整交互流程 (Step-by-Step)
我们可以将流程分为四个阶段:生成二维码 -> 扫描二维码 -> 确认登录 -> 登录成功。
阶段 1:生成二维码 (PC -> Server)
- 用户打开 PC 端登录页面,选择“扫码登录”。
- PC 端向服务端发起请求,获取二维码 ID(
qr_id或uuid)。 - 服务端生成一个唯一的、随机的 ID,将其存入缓存(如 Redis),并设置过期时间(例如 2 分钟)。初始状态为
WAITING(待扫描)。 - 服务端将这个 ID 返回给 PC 端。
- PC 端根据这个 ID 生成二维码图片(通常是一个包含 ID 的 URL,如
https://app.com/scan?id=xyz),展示给用户。 - 关键动作: PC 端开始监听这个 ID 的状态(通过轮询或 WebSocket)。
阶段 2:扫描二维码 (Mobile -> Server)
- 用户打开手机 App,扫描 PC 端的二维码,解析出 ID。
- 手机端将 ID 和自身的
Auth Token(用户身份凭证)发送给服务端。 - 服务端校验手机端的 Token 是否有效。
- 若有效,服务端将 Redis 中该 ID 的状态更新为
SCANNED(已扫描),并可能记录下扫码者的头像/昵称(用于在 PC 端展示,提升体验)。 - PC 端反馈: PC 端监听到状态变为
SCANNED,页面提示“扫描成功,请在手机上确认”。
阶段 3:确认登录 (Mobile -> Server)
- 手机端跳转到“确认登录”页面,显示“是否允许登录 PC 端?”。
- 用户点击“确认登录”按钮。
- 手机端向服务端发送请求:确认授权 ID
xyz登录。 - 服务端收到请求后:
- 生成一个用于 PC 端登录的临时凭证(
temp_token或code)。 - 将 Redis 中该 ID 的状态更新为
CONFIRMED(已确认)。 - 将
temp_token预存或直接关联到该 ID 的数据中。
- 生成一个用于 PC 端登录的临时凭证(
阶段 4:登录成功 (PC <-> Server)
- PC 端监听到状态变为
CONFIRMED,并同时获取到了服务端返回的temp_token。 - PC 端使用这个
temp_token再次请求服务端,换取真正的长效登录凭证(如Session ID或JWT)。 - 服务端校验
temp_token有效性,通过后返回用户信息和登录 Cookie/Token。 - PC 端保存凭证,跳转至主页,登录完成。
三、 技术实现细节
1. 二维码 ID 的状态机
服务端通常使用 Redis 存储 ID 的状态。一个 ID 的生命周期包含以下状态:
- NOT_SCANNED (0): 初始状态,等待扫描。
- SCANNED (1): 已扫描,等待用户点击确认。此时 PC 端通常会显示用户的头像。
- CONFIRMED (2): 用户已确认。此时包含用于换取登录凭证的临时 Token。
- EXPIRED (-1): 二维码超时失效。
- CANCELED (-2): 用户在手机端点击了“取消”。
2. PC 端如何监听状态? (三种主流方案)
这是技术实现中最关键的部分,决定了用户体验的流畅度。
方案 A:短轮询 (Short Polling)
- 做法: PC 端每隔 1-2 秒发送一个 HTTP 请求询问 Server:“ID 状态变了吗?”
- 优点: 实现简单,兼容性好。
- 缺点: 延迟高,服务器压力大(大量无效请求)。
- 适用: 早期方案,现在较少使用。
方案 B:长轮询 (Long Polling) —— 推荐
- 做法: PC 端发送请求,服务端将请求挂起(Hold)。直到状态发生变化(被扫描、被确认、超时),服务端才返回响应。PC 端收到响应后立刻发起下一次请求。
- 优点: 实时性好,减少了无效请求次数。
- 缺点: 占用服务端连接数。
- 适用: 微信网页版、大多数扫码登录均采用此方案。通常设置 25-30 秒超时。
方案 C:WebSocket
- 做法: PC 端与服务端建立全双工 TCP 连接。状态变化时,服务端主动推送消息给 PC 端。
- 优点: 性能最好,延迟最低,通信开销小。
- 缺点: 开发维护成本稍高,需要处理断线重连。
- 适用: 对实时性要求极高或已有 WebSocket 架构的应用(如 Telegram, Discord)。
3. 为什么需要“换取 Token”这一步?
在“阶段 4”中,为什么 PC 端拿到 CONFIRMED 状态后,不直接给它 Session,而是给一个 temp_token 让它再去换?
- 安全性: 监听接口(轮询/Socket)通常是公开的或安全性较低的。如果在监听的响应包里直接返回用户的 Session ID,容易被中间人攻击或日志泄露。
- 一次性验证:
temp_token是一次性的,用完即焚,极大降低了被劫持的风险。
四、 安全性设计 (Security Considerations)
扫码登录虽然方便,但也存在安全风险(如 QRLJacking 劫持攻击),必须注意以下几点:
二维码时效性 (TTL):
二维码 ID 必须设置较短的过期时间(如 60-120 秒)。过期后 PC 端需重新请求生成新码,防止二维码被恶意截获后长时间利用。Token 绑定与一次性:
用于 PC 登录的temp_token必须是一次性的,且有效期极短(如 30 秒)。上下文信息展示 (防钓鱼):
在手机端“确认登录”的页面,必须展示 PC 端的地理位置、设备类型、甚至 IP 地址。- 场景: 攻击者把自己的 PC 二维码发给受害者,骗受害者扫描。
- 防御: 受害者手机上显示“正在尝试登录 广东深圳 的 Windows 设备”,如果受害者人在北京,就会警觉并取消。
HTTPS:
全链路必须使用 HTTPS 加密,防止 ID 和 Token 在传输层被窃听。
五、 总结:时序图描述
PC端 服务端(Redis) 手机端
| | |
|--1. 请求二维码ID ----->| |
|<--2. 返回ID (uuid) ----| |
| | |
|--3. 展示二维码(uuid) | |
| | |
|--4. 长轮询(uuid) ----->| (Hold住请求...) |
| | |
| |<--5. 扫码(uuid + Token)---|
| | 验证Token, 状态=SCANNED |
|<--6. 返回(已扫描) -----| |
| | |
|--7. 再次长轮询(uuid) ->| (Hold住请求...) |
| | |
| |<--8. 确认登录 ------------|
| | 状态=CONFIRMED, 生成code |
|<--9. 返回(code) -------| |
| | |
|--10. 用code换Session ->| |
|<--11. 返回登录Cookie --| |
| | |
|== 登录成功 ============| |
这就是 PC 端扫码登录完整的技术实现逻辑。它巧妙地利用了手机端已有的信任关系,通过一个共享的临时 ID,安全地将信任传递给了 PC 端。