Article

Penny Lens 认证系统完整文档

2026年6月25日 · 5 分钟阅读 · ixNieStudio

整合前端认证与服务端认证的完整文档,涵盖多平台登录、JWT 会话管理、QR 码跨设备登录、安全机制等核心功能。

认证系统完整文档

最后更新:2026-06-25
版本:v1.0
范围:前端认证 + 服务端认证 + 安全机制


一、系统概述

Penny Lens 认证系统采用前后端分离架构,支持多平台登录(微信、支付宝、邮箱/密码)、QR 码跨设备登录、JWT Token 会话管理等核心功能。

核心能力

能力描述
多平台登录微信、支付宝、邮箱/密码
QR 码登录手机扫码登录 PC 端
JWT 会话Token 生成、验证、刷新、失效
安全机制bcrypt 密码加密、请求频率限制、IP 异常检测

二、前端认证模块

2.1 模块架构

src/
├── pages/auth/ # 认证页面
│ ├── index.vue # 身份令牌页面
│ └── qr-login.vue # 二维码登录页面
├── pages/mine/login.vue # 登录/注册页面
├── store/user.ts # 用户状态管理
├── services/api/user.ts # 用户API服务
├── services/api/auth.ts # 认证API服务
├── utils/auth.ts # 认证工具函数
├── utils/uniapi/auth.ts # UniApp认证API
├── utils/uniapi/platformLogin.ts # 平台登录工具
├── types/user.ts # 用户类型定义
├── types/auth.ts # 认证类型定义
└── router/guard.ts # 路由守卫

2.2 登录方式

邮箱/密码登录

流程:用户输入 → 前端验证 → 发送请求 → 后端验证 → 返回 JWT → 保存 Token

核心接口

interface LoginRequest {
username: string; // 用户名或邮箱
password: string; // 密码
deviceId: string; // 设备ID
deviceInfo: string; // 设备信息
}
interface LoginResponse {
token: string; // JWT令牌
userInfo: UserInfo; // 用户信息
expiresIn: number; // 过期时间(秒)
}

微信登录

流程:调用微信授权 API → 获取授权码 → 发送到后端 → 获取用户信息 → 返回 JWT

async function wechatLogin(): Promise<LoginResponse> {
const authResult = await uni.login({ provider: 'weixin' })
if (authResult[1].code) {
const response = await authApi.wechatLogin({
code: authResult[1].code,
deviceId: getDeviceId(),
deviceInfo: getDeviceInfo()
})
if (response.code === 200) {
setToken(response.data.token)
setUserInfo(response.data.userInfo)
return response.data
}
}
throw new Error('微信登录失败')
}

支付宝登录

流程与微信登录类似:调用 uni.login({ provider: 'alipay' }) 获取 authCode,发送到后端换取用户信息并返回 JWT 令牌。

2.3 QR 码跨设备登录

功能:允许用户通过手机小程序扫码在 PC 端完成登录,支持微信和支付宝两个平台。

登录流程

PC端(被扫端) 服务端 手机端(扫码端)
| | |
|-- 1. 生成二维码 ---------->| |
|<-- 2. 返回二维码URL ------| |
| | |
| |<-- 3. 扫码确认 ----------|
| |-- 4. 返回确认结果 ------->|
| | |
|-- 5. 轮询登录状态 ------->| |
|<-- 6. 返回登录结果 -------| |

轮询状态说明

状态说明下一步操作
pending等待扫码PC端继续轮询
scanned已扫码,等待确认PC端继续轮询
confirmed已确认,登录成功PC端停止轮询,使用返回的token
expired会话已过期PC端重新生成二维码
cancelled会话已取消PC端重新生成二维码

实现要点

  • PC 端:调用 user.generateQrCodeLogin 生成会话,每 2 秒轮询 user.pollQrCodeLogin
  • 小程序端:使用 wx.scanCode()(微信)或 my.scan()(支付宝)扫描二维码
  • 二维码默认 5 分钟过期

三、服务端认证系统

3.1 数据模型

用户模型 (User)

interface User {
_id?: string; // 用户唯一标识
username: string; // 用户名
password: string; // 加密后的密码
email?: string; // 邮箱
avatar?: string; // 头像 URL
phoneNumber?: string; // 电话号码
userType: UserType; // 用户类型
status: UserStatus; // 用户状态
createdAt: number; // 创建时间戳
updatedAt: number; // 更新时间戳
delFlag: boolean; // 软删除标记
lastLoginAt?: number; // 最后登录时间
currentSessionId?: string; // 当前会话ID
}

会话模型 (Session)

interface Session {
_id?: string; // 会话ID
userId: string; // 关联的用户ID
token: string; // JWT Token
deviceId?: string; // 设备唯一标识
deviceInfo?: string; // 设备信息
ipAddress?: string; // IP地址
loginTime: number; // 登录时间
expireTime: number; // 过期时间
status: SessionStatus; // 会话状态
platform?: LoginPlatform; // 登录平台
}

3.2 统一登录服务

UnifiedLoginService 是认证系统的核心服务,提供统一的登录入口和会话管理功能。

class UnifiedLoginService extends BaseService {
public async login(params: UnifiedLoginRequest): Promise<UserResponse> {}
public async verifyToken(token: string): Promise<VerifyTokenResult> {}
public async refreshToken(token: string): Promise<string> {}
public async logout(token: string): Promise<void> {}
}

3.3 登录流程

  1. 请求处理:客户端发送请求到 /api,指定 action: "user.login"
  2. 路由分发router.ts 根据 action 找到对应的 UserController.login 方法
  3. 参数验证:验证请求参数的合法性
  4. 登录处理:调用 UnifiedLoginService.login 处理登录逻辑
  5. 平台认证:根据 platform 字段调用相应的第三方认证方法
  6. 用户匹配:查找或创建对应的用户记录
  7. 会话创建:生成 JWT Token,创建新的会话记录
  8. 响应返回:返回包含用户信息和 Token 的响应

3.4 Token 验证流程

  1. 请求拦截:对于需要认证的路由,提取 Token
  2. Token 解析:使用 jsonwebtoken 库解析 Token
  3. Token 验证:验证签名和过期时间
  4. 会话验证:验证 Token 对应的会话是否有效
  5. 用户验证:验证用户状态是否正常
  6. 权限检查:检查用户是否有权限访问请求的资源

四、安全机制

4.1 密码安全

  • 密码加密:使用 bcrypt 算法对用户密码进行加密存储,不可逆
  • 密码策略:建议实施密码复杂度要求(长度、字符组合等)
  • 密码历史:可选实现密码历史记录,避免重复使用旧密码
  • 敏感操作验证:重要操作需要重新验证密码

4.2 Token 安全

  • HTTPS 传输:所有含 Token 的请求必须通过 HTTPS 协议传输
  • Token 存储:客户端应使用安全的方式存储 Token,避免 XSS 攻击
  • Token 验证:服务端每次请求都验证 Token 的有效性
  • Token 泄露处理:支持主动失效特定 Token

4.3 安全防护

  • 输入验证:所有用户输入都进行严格的验证和过滤
  • 请求频率限制:对登录等敏感操作实施请求频率限制,防止暴力破解
  • 异常日志:记录登录失败、异常访问等安全相关事件
  • IP 异常检测:检测并处理异常 IP 访问

4.4 会话控制

  • 多设备登录:支持同一账号在多个设备上登录
  • 会话上限:可配置单个账号的最大会话数
  • 会话踢出:支持强制下线指定会话
  • 会话状态:会话可处于活跃、已过期、已销毁等状态

4.5 Token 刷新机制

当 Token 即将过期时,客户端可以使用当前有效的 Token 请求刷新,获取新的 Token 而无需重新登录。

刷新条件

  • 当前 Token 仍在有效期内
  • Token 对应的会话仍处于活跃状态
  • 用户状态正常

五、API 接口

5.1 统一登录接口

请求

{
"action": "user.login",
"params": {
"platform": "email",
"email": "user@example.com",
"password": "password123",
"deviceId": "device123",
"deviceInfo": "iOS 15.0, iPhone 13"
}
}

响应

{
"code": 200,
"message": "登录成功",
"data": {
"id": "61c0c50b6d1b2c001fd2a345",
"username": "user123",
"email": "user@example.com",
"avatar": "https://example.com/avatar.jpg",
"token": "eyJhbG...VCJ9...",
"createdAt": 1639149835278,
"updatedAt": 1642345678901
}
}

5.2 Token 验证接口

请求

{
"action": "user.verifyToken",
"params": {
"token": "eyJhbG...VCJ9..."
}
}

5.3 注销登录接口

请求

{
"action": "user.logout",
"params": {}
}

六、错误处理

常见错误类型

错误码错误类型错误消息处理建议
400ValidationException参数验证失败检查输入参数是否正确
401UnauthorizedException未授权访问请先登录获取有效Token
403ForbiddenException权限不足检查用户权限
404NotFoundException用户不存在确认用户是否已注册
500DatabaseException数据库操作失败稍后重试或联系管理员

七、前端集成指南

Token 管理

export const tokenUtils = {
setToken(token: string): void {
uni.setStorageSync('token', token)
},
getToken(): string | null {
return uni.getStorageSync('token') || null
},
removeToken(): void {
uni.removeStorageSync('token')
},
isTokenExpired(token: string): boolean {
try {
const payload = JSON.parse(atob(token.split('.')[1]))
return Date.now() >= payload.exp * 1000
} catch { return true }
}
}

路由守卫

export const authGuard = {
checkAuth(): boolean { return sessionManager.isSessionValid() },
requireAuth(path: string): boolean {
const authRequiredPaths = [
'/pages/mine/profile-settings',
'/pages/mine/account-settings',
'/pages/mine/user-sessions',
'/pages/auth/index'
]
return authRequiredPaths.includes(path)
},
beforeRouteEnter(to: any, from: any, next: any): void {
if (authGuard.requireAuth(to.path)) {
if (authGuard.checkAuth()) { next() }
else { uni.navigateTo({ url: '/pages/mine/login?redirect=' + encodeURIComponent(to.path) }) }
} else { next() }
}
}

本文档整合了前端认证模块和服务端认证系统的完整内容。