认证模块
🔐 概述
认证模块是 Penny Lens 应用的核心安全模块,负责用户身份验证、多平台登录、会话管理等功能。支持用户名/密码、微信、支付宝等多种登录方式。
🏗️ 模块架构
核心组件
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 # 路由守卫🔑 认证方式
1. 用户名/密码登录
功能描述: 传统的用户名和密码登录方式
实现流程:
- 用户输入用户名和密码
- 前端验证输入格式
- 发送登录请求到后端
- 后端验证用户凭据
- 返回JWT令牌和用户信息
- 前端保存令牌和用户信息
代码示例:
// 登录请求
interface LoginRequest {
username: string; // 用户名或邮箱
password: string; // 密码
deviceId: string; // 设备ID
deviceInfo: string; // 设备信息
}
// 登录响应
interface LoginResponse {
token: string; // JWT令牌
userInfo: UserInfo; // 用户信息
expiresIn: number; // 过期时间(秒)
}
// 登录实现
async function login(credentials: LoginRequest): Promise<LoginResponse> {
const response = await userApi.login(credentials)
if (response.code === 200) {
// 保存令牌
setToken(response.data.token)
// 保存用户信息
setUserInfo(response.data.userInfo)
return response.data
}
throw new Error(response.message)
}2. 微信登录
功能描述: 通过微信授权登录
实现流程:
- 调用微信授权API获取授权码
- 发送授权码到后端
- 后端通过授权码获取微信用户信息
- 创建或绑定用户账号
- 返回JWT令牌和用户信息
代码示例:
// 微信登录
async function wechatLogin(): Promise<LoginResponse> {
try {
// 获取微信授权码
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('微信登录失败')
} catch (error) {
console.error('微信登录错误:', error)
throw error
}
}3. 支付宝登录
功能描述: 通过支付宝授权登录
实现流程:
- 调用支付宝授权API获取授权码
- 发送授权码到后端
- 后端通过授权码获取支付宝用户信息
- 创建或绑定用户账号
- 返回JWT令牌和用户信息
代码示例:
// 支付宝登录
async function alipayLogin(): Promise<LoginResponse> {
try {
// 获取支付宝授权码
const authResult = await uni.login({
provider: 'alipay'
})
if (authResult[1].authCode) {
// 发送授权码到后端
const response = await authApi.alipayLogin({
authCode: authResult[1].authCode,
deviceId: getDeviceId(),
deviceInfo: getDeviceInfo()
})
if (response.code === 200) {
setToken(response.data.token)
setUserInfo(response.data.userInfo)
return response.data
}
}
throw new Error('支付宝登录失败')
} catch (error) {
console.error('支付宝登录错误:', error)
throw error
}
}4. 二维码登录
功能描述: 通过扫描二维码快速登录
实现流程:
- 生成登录二维码
- 用户扫描二维码
- 确认登录操作
- 完成登录流程
代码示例:
// 二维码登录
interface QRLoginSession {
qrCode: string; // 二维码内容
sessionKey: string; // 会话密钥
expiresIn: number; // 过期时间
}
// 生成登录二维码
async function generateQRCode(): Promise<QRLoginSession> {
const response = await authApi.generateQRCode()
return response.data
}
// 检查登录状态
async function checkQRLoginStatus(sessionKey: string): Promise<QRLoginStatus> {
const response = await authApi.checkQRLoginStatus({ sessionKey })
return response.data
}
// 确认登录
async function confirmQRLogin(sessionKey: string): Promise<LoginResponse> {
const response = await authApi.confirmQRLogin({
sessionKey,
deviceId: getDeviceId(),
deviceInfo: getDeviceInfo()
})
if (response.code === 200) {
setToken(response.data.token)
setUserInfo(response.data.userInfo)
return response.data
}
throw new Error(response.message)
}👤 用户管理
用户注册
功能描述: 新用户注册账号
实现流程:
- 用户填写注册信息
- 前端验证输入格式
- 发送注册请求到后端
- 后端创建用户账号
- 返回JWT令牌和用户信息
代码示例:
// 注册请求
interface RegisterRequest {
username: string; // 用户名(唯一)
password: string; // 密码
email?: string; // 邮箱(可选)
deviceId: string; // 设备ID
deviceInfo: string; // 设备信息
}
// 注册实现
async function register(userData: RegisterRequest): Promise<LoginResponse> {
const response = await userApi.register(userData)
if (response.code === 200) {
setToken(response.data.token)
setUserInfo(response.data.userInfo)
return response.data
}
throw new Error(response.message)
}用户信息管理
功能描述: 获取和更新用户信息
代码示例:
// 获取用户信息
async function getUserInfo(): Promise<UserInfo> {
const response = await userApi.getUserInfo()
return response.data
}
// 更新用户信息
async function updateUserInfo(userData: Partial<UserInfo>): Promise<UserInfo> {
const response = await userApi.updateUserInfo(userData)
if (response.code === 200) {
// 更新本地用户信息
updateLocalUserInfo(response.data)
return response.data
}
throw new Error(response.message)
}账号绑定管理
功能描述: 管理多种登录方式的绑定
代码示例:
// 获取绑定状态
async function getBindStatus(): Promise<BindStatus> {
const response = await userApi.getBindStatus()
return response.data
}
// 邮箱绑定
async function bindEmail(email: string, password: string): Promise<void> {
const response = await userApi.bindEmail({ email, password })
if (response.code !== 200) {
throw new Error(response.message)
}
}
// 邮箱解绑
async function unbindEmail(): Promise<void> {
const response = await userApi.unbindEmail()
if (response.code !== 200) {
throw new Error(response.message)
}
}
// 平台绑定
async function bindPlatform(platform: 'wechat' | 'alipay', authCode: string): Promise<void> {
const response = await userApi.platformBind({ platform, authCode })
if (response.code !== 200) {
throw new Error(response.message)
}
}
// 平台解绑
async function unbindPlatform(platform: 'wechat' | 'alipay'): Promise<void> {
const response = await userApi.platformUnbind({ platform })
if (response.code !== 200) {
throw new Error(response.message)
}
}🔒 安全机制
Token管理
功能描述: JWT令牌的生成、验证、刷新和失效
代码示例:
// 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
}
},
// 刷新令牌
async refreshToken(): Promise<string> {
const refreshToken = uni.getStorageSync('refreshToken')
if (!refreshToken) {
throw new Error('没有刷新令牌')
}
const response = await authApi.refreshToken({ refreshToken })
if (response.code === 200) {
setToken(response.data.token)
return response.data.token
}
throw new Error(response.message)
}
}会话管理
功能描述: 用户会话的创建、维护和销毁
代码示例:
// 会话管理
export const sessionManager = {
// 创建会话
createSession(userInfo: UserInfo, token: string): void {
tokenUtils.setToken(token)
setUserInfo(userInfo)
setLoginTime(Date.now())
},
// 销毁会话
destroySession(): void {
tokenUtils.removeToken()
removeUserInfo()
removeLoginTime()
},
// 检查会话有效性
isSessionValid(): boolean {
const token = tokenUtils.getToken()
if (!token) return false
if (tokenUtils.isTokenExpired(token)) {
// 尝试刷新令牌
try {
await tokenUtils.refreshToken()
return true
} catch {
this.destroySession()
return false
}
}
return true
}
}设备管理
功能描述: 设备信息的收集和管理
代码示例:
// 设备管理
export const deviceManager = {
// 生成设备ID
generateDeviceId(): string {
let deviceId = uni.getStorageSync('deviceId')
if (!deviceId) {
deviceId = 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
uni.setStorageSync('deviceId', deviceId)
}
return deviceId
},
// 获取设备信息
getDeviceInfo(): string {
const systemInfo = uni.getSystemInfoSync()
return JSON.stringify({
platform: systemInfo.platform,
system: systemInfo.system,
version: systemInfo.version,
model: systemInfo.model,
brand: systemInfo.brand
})
},
// 获取设备列表
async getDeviceList(): Promise<Device[]> {
const response = await userApi.getDeviceList()
return response.data
},
// 退出所有设备
async logoutAllDevices(): Promise<void> {
const response = await userApi.logoutAllDevices()
if (response.code !== 200) {
throw new Error(response.message)
}
}
}🛡️ 路由守卫
功能描述: 保护需要认证的页面和功能
代码示例:
// 路由守卫
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()
}
}
}📱 多平台适配
平台检测
功能描述: 检测当前运行平台并适配相应功能
代码示例:
// 平台检测
export const platformDetector = {
// 获取当前平台
getCurrentPlatform(): Platform {
// #ifdef MP-WEIXIN
return 'weixin'
// #endif
// #ifdef MP-ALIPAY
return 'alipay'
// #endif
// #ifdef H5
return 'h5'
// #endif
// #ifdef APP
return 'app'
// #endif
return 'unknown'
},
// 检查平台功能支持
isFeatureSupported(feature: string): boolean {
const platform = this.getCurrentPlatform()
const featureMap = {
wechat: ['login', 'payment', 'share'],
alipay: ['login', 'payment', 'share'],
h5: ['login', 'share'],
app: ['login', 'push', 'native']
}
return featureMap[platform]?.includes(feature) || false
}
}平台登录适配
功能描述: 根据平台提供相应的登录方式
代码示例:
// 平台登录适配
export const platformLoginAdapter = {
// 获取支持的登录方式
getSupportedLoginMethods(): LoginMethod[] {
const platform = platformDetector.getCurrentPlatform()
const methods: LoginMethod[] = ['username']
if (platformDetector.isFeatureSupported('wechat')) {
methods.push('wechat')
}
if (platformDetector.isFeatureSupported('alipay')) {
methods.push('alipay')
}
return methods
},
// 执行平台登录
async executePlatformLogin(method: LoginMethod): Promise<LoginResponse> {
switch (method) {
case 'username':
throw new Error('用户名登录需要手动调用')
case 'wechat':
return await wechatLogin()
case 'alipay':
return await alipayLogin()
default:
throw new Error('不支持的登录方式')
}
}
}🔄 状态管理
Pinia Store
功能描述: 使用Pinia管理用户认证状态
代码示例:
// 用户状态管理
export const useUserStore = defineStore('user', () => {
// 状态
const userInfo = ref<UserInfo | null>(null)
const isLoggedIn = computed(() => !!userInfo.value)
const loginPlatform = ref<LoginPlatform | null>(null)
// 操作
const login = async (credentials: LoginCredentials): Promise<void> => {
try {
const response = await userService.login(credentials)
userInfo.value = response.userInfo
loginPlatform.value = response.loginPlatform
sessionManager.createSession(response.userInfo, response.token)
} catch (error) {
console.error('登录失败:', error)
throw error
}
}
const logout = async (): Promise<void> => {
try {
await userService.logout()
} catch (error) {
console.error('登出失败:', error)
} finally {
userInfo.value = null
loginPlatform.value = null
sessionManager.destroySession()
}
}
const updateUserInfo = async (data: Partial<UserInfo>): Promise<void> => {
try {
const updatedInfo = await userService.updateUserInfo(data)
userInfo.value = updatedInfo
} catch (error) {
console.error('更新用户信息失败:', error)
throw error
}
}
const initUserInfo = async (): Promise<UserInfo | null> => {
try {
if (sessionManager.isSessionValid()) {
const info = await userService.getUserInfo()
userInfo.value = info
return info
}
} catch (error) {
console.error('初始化用户信息失败:', error)
sessionManager.destroySession()
}
return null
}
return {
userInfo,
isLoggedIn,
loginPlatform,
login,
logout,
updateUserInfo,
initUserInfo
}
})📚 最佳实践
1. 安全最佳实践
- 使用HTTPS传输敏感数据
- 定期刷新JWT令牌
- 实现设备管理功能
- 记录登录日志
- 实现账号锁定机制
2. 用户体验最佳实践
- 提供多种登录方式
- 记住用户登录状态
- 提供快速登录选项
- 友好的错误提示
- 支持离线登录
3. 开发最佳实践
- 统一的错误处理
- 完整的类型定义
- 充分的测试覆盖
- 清晰的代码注释
- 模块化设计
Penny Lens 认证模块 - 让用户身份管理更安全、更便捷 🔐
