一、核心流程架构
-
三方协作模型
- Web端:生成/展示二维码 + 轮询状态
- 移动端:扫码 + 授权确认
- 服务端:协调认证流程 + 状态同步
-
关键步骤时序
Web前端->>服务端: 1.请求生成二维码(带UUID) 服务端-->>Web前端: 2.返回二维码数据(有效期120秒) Web前端->>服务端: 3.开始轮询状态(间隔2秒) 移动端->>服务端: 4.扫码提交UUID+token 服务端->>移动端: 5.返回授权确认页 移动端->>服务端: 6.用户点击确认登录 服务端-->>Web前端: 7.轮询返回登录成功 Web前端->>服务端: 8.获取用户凭证完成登录
二、关键代码实现
QRCode插件是前端开发中用于生成二维码的核心工具,其核心作用和功能如下:
一、核心功能
-
文本转二维码
将URL、文本、JSON等数据编码为二维码图形,支持动态内容生成// 示例:将URL转为二维码图片 QRCode.toCanvas('https://example.com', { width: 200 })
-
自定义渲染控制
- 支持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)
})
三、实战优化方案
-
安全增强
- 在移动端确认页显示登录设备IP和地理位置
-
- 异常登录识别
通过对比用户常用设备IP与当前登录IP的差异,可快速发现异地登录等可疑行为14。例如:若用户常驻北京却突然出现海南登录记录,系统将触发风险提示。
- 异常登录识别
-
- 双重确认机制
地理位置信息作为二次验证要素,帮助用户确认是否为本人操作4。数据显示该功能可降低约37%的账号盗用风险。
- 双重确认机制
- 对UUID进行JWT签名防止伪造
-
- 签名验证真实性
通过JWT的签名算法(如HS256/RSA256)对UUID进行加密,确保该标识符只能由持有密钥的服务端生成,第三方无法伪造有效令牌34。若攻击者篡改UUID值,签名校验将立即失败48。
- 签名验证真实性
-
- 抵御中间人攻击
JWT签名可防止传输过程中UUID被截获后恶意复用,确保扫码登录流程的端到端完整性
- 抵御中间人攻击
-
-
性能优化
- 采用WebSocket替代HTTP轮询(当在线用户>1万时)
- 服务端使用Redis集群存储会话状态
-
异常处理
// 关键点8:完善的错误处理 const handlePollError = (error) => { if (error.response?.status === 410) { showExpiredTip() } else { // 采用指数退避重试 currentDelay = Math.min(currentDelay * 2, 30000) setTimeout(startPolling, currentDelay) } }
-
多平台适配
- 微信扫码需使用官方JS-SDK
- 企业微信需处理corpId与agentId绑定