扫码登录完整实现指南 - 从需求分析到前端实现

admin 6989

扫码登录完整实现指南 - 从需求分析到前端实现

前言

扫码登录已成为当前主流应用的标配功能,从微信、钉钉到各种企业应用,这种登录方式既提高了用户体验,又增强了账户安全性。本文将从需求分析开始,详细介绍扫码登录功能的完整实现流程,包括接口设计、前端实现以及状态管理等内容。无论你是否接触过相关业务,阅读本文都能帮助你快速掌握扫码登录的实现要点。

需求分析

首先,我们来理清基本需求:

登录二维码有效期默认2分钟,超过2分钟提示用户刷新

扫码成功后,跳转扫码成功页面,用户在移动端点击确认登录后,登录进入应用

需求理解

关于二维码有效期: 在生成登录二维码时,后端服务器将生成一个二维码标识符,并设置一个2分钟的有效期。后端可以通过时间戳或其他方法来跟踪二维码的有效性。前端在展示二维码的同时也开始计时,在二维码有效期结束前,如果用户未完成扫码登录,提示用户刷新二维码。

关于扫码登录流程: 一旦用户在移动设备上成功扫描二维码,并点击确认登录按钮,前端应轮询向后端发送请求,确认用户的登录操作。

流程设计

根据需求分析,扫码登录的完整流程如下:

sequenceDiagram

participant PC as PC端

participant Server as 服务端

participant Mobile as 移动端

PC->>Server: 请求生成二维码

Server-->>PC: 返回二维码数据

Note over PC: 展示二维码,开始轮询查询状态

loop 每隔几秒

PC->>Server: 查询二维码状态

Server-->>PC: 返回状态(未扫描)

end

Mobile->>PC: 扫描二维码

Mobile->>Server: 发送扫码确认请求

Server-->>Mobile: 返回确认结果

PC->>Server: 查询二维码状态

Server-->>PC: 返回状态(已扫描)

Mobile->>Server: 确认登录

Server-->>Mobile: 确认成功

PC->>Server: 查询二维码状态

Server-->>PC: 返回状态(已确认)

Note over PC: 登录成功,进入应用接口设计

根据流程图,我们需要设计以下三个核心接口:

1. 二维码生成接口

接口命名: POST: /v1/accounts/qrcode/

请求参数:

typescript

复制代码

export interface Request {

/**

* 重新生成/取消时需要给

*/

code?: string;

platform: string;

}

平台类型:

arduino

复制代码

GKOL_PC // xx PC端

GKOL_WEB // xx Web端

GKOL_WEB_MANAGER // xx管理端

GKOL_WEB_OPERATE // xx后台

GKOL_WEB_ORGANIZE // xx后台

响应结构:

typescript

复制代码

export interface Response {

/**

* 二维码,前缀+base64串,格式:rk://scanforpclogin/{qrcode}

* 二维码json解析结构

* {

* "id":"",

* "expire":1685696389,

* "prefix":"rk://scanforpclogin/"

* "platform":"Rxx_PC"

* }

*/

png: string;

}

2. 扫描二维码接口

接口命名: POST: /v1/accounts/qrcode_fill

说明: 主要给移动端用来扫码调用。移动端先扫码web端的二维码图片,解析得到id,然后将id作为参数传入该接口。

请求参数: 可以省略,用户信息放在header的token中

响应结构:

typescript

复制代码

export interface Response {

/**

* 身份卡ID,为空就不限制

*/

card_id: string;

/**

* 二维码令牌,二维码解析结果

*/

id: string;

/**

* 状态,SCAN 已扫描

* VERIFY 已确认

* CANCEL 取消

*/

step: string;

}

3. 账号登录状态查询接口

接口命名: POST /v1/passport/guest

说明: Web端通过轮询该接口,监听移动端的扫码状态。

请求参数:

typescript

复制代码

export interface Request {

/**

* 二维码唯一标识

*/

code: string;

/**

* 登录类型,QRCODE表示二维码登录

*/

type: string;

}

响应结构:

typescript

复制代码

export interface Response {

/**

* 登录令牌

*/

token: string;

/**

* 状态码

* QRCODE_SUCCESS: 成功

* QRCODE_ERROR: 错误

* QRCODE_EXPIRE: 过期

*/

reason: string;

/**

* 用户信息

*/

user_info: UserInfo;

}

前端实现

1. 生成并展示二维码

首先,调用二维码生成接口,然后解析返回的base64数据:

javascript

复制代码

import qrcodeParser from 'qrcode-parser';

const generateQRCode = async () => {

try {

const response = await V1CreateQRcode({

platform: 'GKOL_PC'

});

if (response.data?.png) {

// 设置二维码图片URL

qrcodeUrl.value = `data:image/png;base64,${response.data.png}`;

// 解析二维码内容

const result = await qrcodeParser(response.data.png);

QrCodeData.value = result ? JSON.parse(result) : null;

console.log('二维码数据:', QrCodeData.value);

// {

// "id": "unique-identifier",

// "expire": 1685696389,

// "prefix": "rk://scanforpclogin/",

// "platform": "Rxx_PC"

// }

// 开始监听扫码状态

startPolling();

}

} catch (error) {

console.error('生成二维码失败:', error);

}

};

2. 封装轮询Hook

为了方便状态管理,我们可以封装一个轮询的自定义Hook:

typescript

复制代码

import { ref, onUnmounted } from 'vue';

export function useQRCodePolling() {

const pollingStatus = ref('WAITING'); // 初始状态:等待扫码

const pollingTimer = ref(null);

const qrCodeExpired = ref(false);

const startPolling = (qrCodeId) => {

if (pollingTimer.value) clearInterval(pollingTimer.value);

// 计算过期时间

const expireTime = Date.now() + 2 * 60 * 1000; // 2分钟后过期

pollingTimer.value = setInterval(async () => {

try {

// 检查是否过期

if (Date.now() > expireTime) {

clearInterval(pollingTimer.value);

qrCodeExpired.value = true;

return;

}

// 调用查询接口

const response = await V1PassportGuest({

code: qrCodeId,

type: 'QRCODE'

});

// 根据返回状态处理

switch(response.data?.reason) {

case 'QRCODE_SUCCESS':

pollingStatus.value = 'SUCCESS';

clearInterval(pollingTimer.value);

// 处理登录成功

handleLoginSuccess(response.data);

break;

case 'QRCODE_ERROR':

pollingStatus.value = 'ERROR';

clearInterval(pollingTimer.value);

// 处理错误

break;

case 'QRCODE_EXPIRE':

pollingStatus.value = 'EXPIRED';

clearInterval(pollingTimer.value);

qrCodeExpired.value = true;

// 处理过期

break;

default:

// 继续等待

break;

}

} catch (error) {

console.error('轮询出错:', error);

}

}, 2000); // 每2秒轮询一次

};

const refreshQRCode = async () => {

qrCodeExpired.value = false;

await generateQRCode(); // 重新生成二维码

};

// 组件卸载时清除定时器

onUnmounted(() => {

if (pollingTimer.value) {

clearInterval(pollingTimer.value);

}

});

return {

pollingStatus,

qrCodeExpired,

startPolling,

refreshQRCode

};

}

3. 完整的二维码登录组件

vue

复制代码

状态管理与用户体验优化

扫码登录过程中有多种状态需要管理,为了提供良好的用户体验,我们需要针对不同状态提供清晰的反馈:

等待扫码:展示二维码和引导文案

已扫描:提示用户在手机上确认

登录成功:展示成功动画,准备跳转

二维码过期:提供刷新按钮

登录失败:展示错误信息,提供重试选项

以下是一个简化的状态管理代码示例:

typescript

复制代码

// 状态定义

enum QRCodeStatus {

WAITING = 'WAITING',

SCANNED = 'SCANNED',

SUCCESS = 'SUCCESS',

ERROR = 'ERROR',

EXPIRED = 'EXPIRED'

}

// 状态对应的UI提示

const statusMessages = {

[QRCodeStatus.WAITING]: '请使用APP扫描二维码登录',

[QRCodeStatus.SCANNED]: '已扫描,请在手机上确认',

[QRCodeStatus.SUCCESS]: '登录成功,正在跳转...',

[QRCodeStatus.ERROR]: '登录失败,请重试',

[QRCodeStatus.EXPIRED]: '二维码已过期,请刷新'

};

// 状态转换处理

const handleStatusChange = (status: QRCodeStatus) => {

currentStatus.value = status;

switch(status) {

case QRCodeStatus.SUCCESS:

// 播放成功动画

playSuccessAnimation();

// 延迟跳转

setTimeout(() => {

router.push('/dashboard');

}, 1500);

break;

case QRCodeStatus.EXPIRED:

// 显示刷新按钮

showRefreshButton.value = true;

break;

// 其他状态处理...

}

};

安全性考虑

在实现扫码登录时,需要注意以下安全性问题:

二维码唯一性:每个二维码应该具有唯一标识,且仅一次有效

有效期控制:设置合理的有效期,防止二维码被长时间使用

防护重放攻击:确保二维码使用后立即失效

用户确认机制:在移动端要求用户明确确认登录操作

设备信息验证:记录并验证登录设备信息

总结

扫码登录的实现涉及多个环节,包括二维码生成、状态轮询、用户确认等。通过本文介绍的方法,我们可以实现一个完整的扫码登录功能,既满足了用户的便捷需求,又能保证登录的安全性。

在实际开发中,可能还需要根据具体业务需求做更多定制,例如:

多端支持(Web、桌面应用、不同移动设备等)

授权范围控制

登录历史记录

异常行为检测

希望本文能对你实现扫码登录功能有所帮助!如有问题或建议,欢迎在评论区留言交流。

参考资料

OAuth 2.0 协议: oauth.net/2/

QR Code 规范: www.qrcode.com/en/about/

Web API - Timers: developer.mozilla.org/en-US/docs/...