317 lines
6.3 KiB
JavaScript
317 lines
6.3 KiB
JavaScript
/**
|
|
* 用户认证和权限管理系统
|
|
*/
|
|
|
|
const { Storage } = require('./storage.js')
|
|
const { logger } = require('./logger.js')
|
|
const { store } = require('./store.js')
|
|
|
|
class AuthManager {
|
|
constructor() {
|
|
this.token = null
|
|
this.userInfo = null
|
|
this.permissions = []
|
|
this.init()
|
|
}
|
|
|
|
/**
|
|
* 初始化
|
|
*/
|
|
init() {
|
|
this.token = Storage.get('token')
|
|
this.userInfo = Storage.get('userInfo')
|
|
this.permissions = Storage.get('permissions', [])
|
|
|
|
if (this.token && this.userInfo) {
|
|
store.setState({
|
|
isLogin: true,
|
|
userInfo: this.userInfo
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 微信登录
|
|
*/
|
|
async wxLogin() {
|
|
try {
|
|
// 1. 获取微信登录code
|
|
const { code } = await this._wxLogin()
|
|
logger.info('微信登录code获取成功', { code })
|
|
|
|
// 2. 获取用户信息
|
|
const userInfo = await this._getUserProfile()
|
|
logger.info('用户信息获取成功', { userInfo })
|
|
|
|
// 3. 调用后端登录接口
|
|
// TODO: 替换为实际的后端登录接口
|
|
const response = await this._mockLogin(code, userInfo)
|
|
|
|
// 4. 保存登录状态
|
|
this.setAuth(response.token, response.userInfo, response.permissions)
|
|
|
|
logger.info('登录成功', { userId: response.userInfo.id })
|
|
|
|
return response
|
|
} catch (error) {
|
|
logger.error('登录失败', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 微信登录 - 获取code
|
|
*/
|
|
_wxLogin() {
|
|
return new Promise((resolve, reject) => {
|
|
wx.login({
|
|
success: resolve,
|
|
fail: reject
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 获取用户信息
|
|
*/
|
|
_getUserProfile() {
|
|
return new Promise((resolve, reject) => {
|
|
wx.getUserProfile({
|
|
desc: '用于完善用户资料',
|
|
success: (res) => resolve(res.userInfo),
|
|
fail: reject
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 模拟登录(开发用)
|
|
*/
|
|
async _mockLogin(code, userInfo) {
|
|
// TODO: 替换为实际的API调用
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve({
|
|
token: 'mock_token_' + Date.now(),
|
|
userInfo: {
|
|
id: Math.random().toString(36).substr(2, 9),
|
|
nickName: userInfo.nickName,
|
|
avatarUrl: userInfo.avatarUrl,
|
|
studentId: '2021001',
|
|
grade: '2021级',
|
|
major: '计算机科学与技术',
|
|
email: 'student@example.com'
|
|
},
|
|
permissions: ['user', 'student']
|
|
})
|
|
}, 500)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 设置认证信息
|
|
*/
|
|
setAuth(token, userInfo, permissions = []) {
|
|
this.token = token
|
|
this.userInfo = userInfo
|
|
this.permissions = permissions
|
|
|
|
Storage.set('token', token)
|
|
Storage.set('userInfo', userInfo)
|
|
Storage.set('permissions', permissions)
|
|
|
|
store.setState({
|
|
isLogin: true,
|
|
userInfo: userInfo
|
|
})
|
|
|
|
logger.info('认证信息已保存')
|
|
}
|
|
|
|
/**
|
|
* 退出登录
|
|
*/
|
|
logout() {
|
|
this.token = null
|
|
this.userInfo = null
|
|
this.permissions = []
|
|
|
|
Storage.remove('token')
|
|
Storage.remove('userInfo')
|
|
Storage.remove('permissions')
|
|
|
|
store.setState({
|
|
isLogin: false,
|
|
userInfo: null
|
|
})
|
|
|
|
logger.info('已退出登录')
|
|
|
|
wx.showToast({
|
|
title: '已退出登录',
|
|
icon: 'success'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 检查是否登录
|
|
*/
|
|
isLogin() {
|
|
return !!this.token && !!this.userInfo
|
|
}
|
|
|
|
/**
|
|
* 获取用户信息
|
|
*/
|
|
getUserInfo() {
|
|
return this.userInfo
|
|
}
|
|
|
|
/**
|
|
* 获取Token
|
|
*/
|
|
getToken() {
|
|
return this.token
|
|
}
|
|
|
|
/**
|
|
* 检查权限
|
|
*/
|
|
hasPermission(permission) {
|
|
return this.permissions.includes(permission)
|
|
}
|
|
|
|
/**
|
|
* 检查多个权限(需要全部满足)
|
|
*/
|
|
hasAllPermissions(permissions) {
|
|
return permissions.every(p => this.hasPermission(p))
|
|
}
|
|
|
|
/**
|
|
* 检查多个权限(满足任一即可)
|
|
*/
|
|
hasAnyPermission(permissions) {
|
|
return permissions.some(p => this.hasPermission(p))
|
|
}
|
|
|
|
/**
|
|
* 更新用户信息
|
|
*/
|
|
async updateUserInfo(updates) {
|
|
try {
|
|
// TODO: 调用后端API更新用户信息
|
|
const updatedUserInfo = { ...this.userInfo, ...updates }
|
|
|
|
this.userInfo = updatedUserInfo
|
|
Storage.set('userInfo', updatedUserInfo)
|
|
store.setState({ userInfo: updatedUserInfo })
|
|
|
|
logger.info('用户信息更新成功', updates)
|
|
|
|
wx.showToast({
|
|
title: '更新成功',
|
|
icon: 'success'
|
|
})
|
|
|
|
return updatedUserInfo
|
|
} catch (error) {
|
|
logger.error('用户信息更新失败', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 刷新Token
|
|
*/
|
|
async refreshToken() {
|
|
try {
|
|
// TODO: 调用后端API刷新token
|
|
const newToken = 'new_token_' + Date.now()
|
|
|
|
this.token = newToken
|
|
Storage.set('token', newToken)
|
|
|
|
logger.info('Token刷新成功')
|
|
|
|
return newToken
|
|
} catch (error) {
|
|
logger.error('Token刷新失败', error)
|
|
this.logout()
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 页面权限守卫
|
|
*/
|
|
class RouteGuard {
|
|
/**
|
|
* 检查页面访问权限
|
|
*/
|
|
static check(requiredPermissions = []) {
|
|
const authManager = new AuthManager()
|
|
|
|
// 检查是否登录
|
|
if (!authManager.isLogin()) {
|
|
wx.showModal({
|
|
title: '提示',
|
|
content: '请先登录',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
wx.navigateTo({
|
|
url: '/pages/login/login'
|
|
})
|
|
} else {
|
|
wx.navigateBack()
|
|
}
|
|
}
|
|
})
|
|
return false
|
|
}
|
|
|
|
// 检查权限
|
|
if (requiredPermissions.length > 0) {
|
|
if (!authManager.hasAllPermissions(requiredPermissions)) {
|
|
wx.showToast({
|
|
title: '没有访问权限',
|
|
icon: 'none'
|
|
})
|
|
wx.navigateBack()
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* 页面装饰器
|
|
*/
|
|
static requireAuth(requiredPermissions = []) {
|
|
return function(target) {
|
|
const originalOnLoad = target.prototype.onLoad
|
|
|
|
target.prototype.onLoad = function(options) {
|
|
if (RouteGuard.check(requiredPermissions)) {
|
|
if (originalOnLoad) {
|
|
originalOnLoad.call(this, options)
|
|
}
|
|
}
|
|
}
|
|
|
|
return target
|
|
}
|
|
}
|
|
}
|
|
|
|
// 创建全局实例
|
|
const authManager = new AuthManager()
|
|
|
|
module.exports = {
|
|
AuthManager,
|
|
authManager,
|
|
RouteGuard
|
|
}
|