Skip to content

Node.js 扫码登录(SCL)

用户通过手机扫描二维码完成 PC 端登录,无需手动输入用户名密码。

优势

优势说明
方便快捷扫码即登录
安全性高加密传输,降低密码泄露风险
防键盘记录无需键盘输入敏感信息
适用广泛统一登录方式,跨平台集成

实现流程

PC端                        服务端                    手机端
  │                           │                         │
  │── GET /qrcode ──────────→│                         │
  │←── 返回二维码(含临时token)  │                         │
  │                           │                         │
  │── 轮询 /check ───────────→│                         │
  │←── { status: 'pending' }  │                         │
  │                           │                         │
  │                           │←── 手机扫码, POST /confirm│
  │                           │    (临时token + 用户token)│
  │                           │── 验证通过, 设置状态为OK    │
  │                           │←── { success: true }     │
  │                           │                         │
  │── 轮询 /check ───────────→│                         │
  │←── { status: 'ok', token }│                         │
  │   PC端拿到JWT完成登录       │                         │

依赖

bash
npm install express jsonwebtoken qrcode

核心代码

生成二维码(PC端调用)

js
import QRCode from 'qrcode'
import { v4 as uuid } from 'uuid'

const qrStore = {} // 临时存储

app.get('/qrcode', (req, res) => {
  const qrcodeId = uuid()
  qrStore[qrcodeId] = { status: 'pending' }  // 初始未扫码
  const qrDataUrl = await QRCode.toDataURL(`http://localhost:3000/confirm?qrcodeId=${qrcodeId}`)
  res.json({ qrcodeId, qrDataUrl })
})

PC端轮询检查

js
app.get('/check', (req, res) => {
  const { qrcodeId } = req.query
  const qrData = qrStore[qrcodeId]
  if (qrData?.status === 'ok') {
    const token = jwt.sign({ userId: qrData.userId }, secret, { expiresIn: '7d' })
    res.json({ status: 'ok', token })
    delete qrStore[qrcodeId]
  } else {
    res.json({ status: 'pending' })
  }
})

手机端确认

js
app.post('/confirm', (req, res) => {
  const { qrcodeId } = req.body
  // 从请求头中获取已登录用户的 token
  const userToken = req.headers.authorization
  const decoded = jwt.verify(userToken, secret)

  qrStore[qrcodeId] = { status: 'ok', userId: decoded.userId }
  res.json({ success: true })
})

关键点

  • 二维码有效期要短(1-2 分钟),过期需刷新
  • 状态可用 Redis 存储(带 TTL)
  • 手机端扫码后需要用户手动确认(防误操作)