前端实现客户端扫码登录的全流程解析及实战案例

0 阅读3分钟

一、核心流程架构

  1. 三方协作模型

    • Web端:生成/展示二维码 + 轮询状态
    • 移动端:扫码 + 授权确认
    • 服务端:协调认证流程 + 状态同步
  2. 关键步骤时序

    Web前端->>服务端: 1.请求生成二维码(带UUID)
    服务端-->>Web前端: 2.返回二维码数据(有效期120秒)
    Web前端->>服务端: 3.开始轮询状态(间隔2秒)
    移动端->>服务端: 4.扫码提交UUID+token
    服务端->>移动端: 5.返回授权确认页
    移动端->>服务端: 6.用户点击确认登录
    服务端-->>Web前端: 7.轮询返回登录成功
    Web前端->>服务端: 8.获取用户凭证完成登录
    

    二、关键代码实现

QRCode插件是前端开发中用于生成二维码的核心工具,其核心作用和功能如下:

一、核心功能
  1. 文本转二维码
    将URL、文本、JSON等数据编码为二维码图形,支持动态内容生成

    // 示例:将URL转为二维码图片
    QRCode.toCanvas('https://example.com', { width: 200 })
    
  2. 自定义渲染控制

    • 支持Canvas/SVG/Image等多种渲染方式
    • 可调整尺寸、边距、颜色等视觉参数
    javascriptCopy Code
    QRCode.toString('hello', { 
      type: 'svg',
      color: { dark: '#123456' }
    }, (err, svg) => {})
    

QRLogin.vue


<script setup>
import { ref, onMounted } from 'vue'
import QRCode from 'qrcode'

// 关键点1:二维码状态机
const states = {
  INIT: 0,      // 初始状态
  SCANNED: 1,   // 已扫码未确认
  CONFIRMED: 2, // 已确认登录
  EXPIRED: 3    // 二维码过期
}

const qrUrl = ref('')
const status = ref(states.INIT)
let pollTimer = null

// 关键点2:生成带UUID的二维码
const generateQR = async () => {
  const { data } = await axios.post('/api/qrcode', {
    platform: 'WEB',
    scene: 'login'
  })
  qrUrl.value = `auth://${data.uuid}`
  await QRCode.toCanvas(document.getElementById('qrcode'), qrUrl.value, {
    width: 200,
    margin: 2
  })
  startPolling(data.uuid)
}

// 关键点3:轮询状态机制
const startPolling = (uuid) => {
  clearInterval(pollTimer)
  pollTimer = setInterval(async () => {
    const { data } = await axios.get(`/api/check?uuid=${uuid}`)
    switch(data.status) {
      case states.SCANNED: 
        showScanTip() // 显示"扫码成功请确认"
        break
      case states.CONFIRMED:
        clearInterval(pollTimer)
        handleLogin(data.token) // 处理登录逻辑
        break
      case states.EXPIRED:
        clearInterval(pollTimer)
        showRefreshButton() // 显示刷新按钮
    }
  }, 2000) // 关键点4:建议轮询间隔2秒
}
</script>

<template>
  <div class="qr-container">
    <!-- 关键点5:扩大点击区域的二维码容器 -->
    <canvas id="qrcode" style="padding: 15px"></canvas>
    <div v-if="status === states.EXPIRED">
      <button @click="generateQR">刷新二维码</button>
    </div>
  </div>
</template>

serve.js

 express = require('express')
const { v4: uuidv4 } = require('uuid')
const app = express()

// 关键点6:使用Redis存储临时状态
const qrCodes = new Map() // 生产环境应替换为Redis

// 生成二维码接口
app.post('/api/qrcode', (req, res) => {
  const uuid = uuidv4()
  qrCodes.set(uuid, { 
    status: 0, 
    createdAt: Date.now(),
    token: null
  })
  // 关键点7:设置120秒过期时间
  setTimeout(() => {
    if (qrCodes.get(uuid)?.status < 2) {
      qrCodes.set(uuid, { status: 3 })
    }
  }, 120000)
  res.json({ uuid })
})

// 移动端扫码回调
app.post('/api/scan', (req, res) => {
  const { uuid, deviceId } = req.body
  if (!qrCodes.has(uuid)) return res.status(410).end()
  qrCodes.set(uuid, { 
    status: 1,
    deviceId 
  })
  res.json({ confirmUrl: '/confirm?uuid='+uuid })
})

// 状态轮询接口
app.get('/api/check', (req, res) => {
  const record = qrCodes.get(req.query.uuid)
  if (!record) return res.status(410).json({ status: 3 })
  res.json(record)
})

三、实战优化方案

  1. 安全增强

    - 在移动端确认页显示登录设备IP和地理位置

      1. 异常登录识别
        通过对比用户常用设备IP与当前登录IP的差异,可快速发现异地登录等可疑行为14。例如:若用户常驻北京却突然出现海南登录记录,系统将触发风险提示。
      1. 双重确认机制
        地理位置信息作为二次验证要素,帮助用户确认是否为本人操作4。数据显示该功能可降低约37%的账号盗用风险。

    - 对UUID进行JWT签名防止伪造

      1. 签名验证真实性
        通过JWT的签名算法(如HS256/RSA256)对UUID进行加密,确保该标识符只能由持有密钥的服务端生成,第三方无法伪造有效令牌34。若攻击者篡改UUID值,签名校验将立即失败48。
      1. 抵御中间人攻击
        JWT签名可防止传输过程中UUID被截获后恶意复用,确保扫码登录流程的端到端完整性
  2. 性能优化

    • 采用WebSocket替代HTTP轮询(当在线用户>1万时)
    • 服务端使用Redis集群存储会话状态
  3. 异常处理

    // 关键点8:完善的错误处理
    const handlePollError = (error) => {
      if (error.response?.status === 410) {
        showExpiredTip()
      } else {
        // 采用指数退避重试
        currentDelay = Math.min(currentDelay * 2, 30000)
        setTimeout(startPolling, currentDelay)
      }
    }
    

1747836264094.jpg

  1. 多平台适配

    • 微信扫码需使用官方JS-SDK
    • 企业微信需处理corpId与agentId绑定