miniprogramme/utils/auth.js
2025-09-12 16:08:17 +08:00

446 lines
No EOL
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 认证工具类 - 参考Flutter的auth_provider.dart
const apiClient = require('./api-client.js');
class AuthManager {
constructor() {
this.isInitialized = false;
this.loginPromise = null; // 防止并发登录
this.tokenRefreshPromise = null; // 防止并发刷新token
this._app = null; // 缓存app实例
}
// 安全获取app实例
getApp() {
if (!this._app) {
try {
this._app = getApp();
} catch (error) {
console.error('获取app实例失败:', error);
return null;
}
}
return this._app;
}
// 初始化认证管理器
async init() {
if (this.isInitialized) {
return;
}
console.log('初始化认证管理器');
try {
// 检查本地存储的用户信息
const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.token) {
console.log('发现本地用户信息,尝试恢复登录状态');
// 检查token是否过期
if (this.isTokenValid(userInfo)) {
// 恢复登录状态
const app = this.getApp();
if (app) {
app.globalData.userInfo = userInfo;
app.globalData.isLoggedIn = true;
}
apiClient.setToken(userInfo.token);
console.log('登录状态恢复成功');
} else {
console.log('Token已过期尝试刷新');
await this.refreshTokenIfNeeded(userInfo);
}
} else {
console.log('未发现本地登录信息');
}
this.isInitialized = true;
console.log('认证管理器初始化完成');
} catch (error) {
console.error('认证管理器初始化失败:', error);
this.clearAuthData();
this.isInitialized = true;
}
}
// 检查token是否有效
isTokenValid(userInfo) {
if (!userInfo || !userInfo.token || !userInfo.expiresAt) {
return false;
}
// 检查是否过期提前5分钟判断过期
const expiresAt = new Date(userInfo.expiresAt).getTime();
const now = Date.now();
const bufferTime = 5 * 60 * 1000; // 5分钟缓冲时间
return (expiresAt - now) > bufferTime;
}
// 检查是否已登录
isLoggedIn() {
const app = this.getApp();
return app?.globalData?.isLoggedIn && !!app?.globalData?.userInfo?.token;
}
// 获取当前用户信息
getCurrentUser() {
const app = this.getApp();
return app?.globalData?.userInfo?.user || null;
}
// 获取完整的认证信息包含token
getFullUserInfo() {
const app = this.getApp();
return app?.globalData?.userInfo || null;
}
// 获取当前token
getCurrentToken() {
const app = this.getApp();
return app?.globalData?.userInfo?.token || null;
}
// 静默登录检查
async ensureAuthenticated() {
if (!this.isInitialized) {
await this.init();
}
if (this.isLoggedIn()) {
console.log('用户已登录');
return true;
}
console.log('用户未登录,需要跳转到登录页');
return false;
}
// 刷新token
async refreshTokenIfNeeded(userInfo) {
// 防止并发刷新
if (this.tokenRefreshPromise) {
return await this.tokenRefreshPromise;
}
if (!userInfo?.refreshToken) {
console.log('没有refresh token无法刷新');
this.clearAuthData();
return false;
}
console.log('开始刷新token');
this.tokenRefreshPromise = this._doRefreshToken(userInfo.refreshToken);
try {
const result = await this.tokenRefreshPromise;
return result;
} finally {
this.tokenRefreshPromise = null;
}
}
// 执行token刷新
async _doRefreshToken(refreshToken) {
try {
const response = await apiClient.refreshToken(refreshToken);
if (response && response.data) {
// 更新用户信息
const app = this.getApp();
const currentUserInfo = app?.globalData?.userInfo || {};
const updatedUserInfo = {
...currentUserInfo,
token: response.data.access_token,
refreshToken: response.data.refresh_token || refreshToken,
expiresAt: response.data.expires_at * 1000, // 转换为毫秒时间戳
loginTime: Date.now()
};
// 保存到本地存储
wx.setStorageSync('userInfo', updatedUserInfo);
// 更新全局状态
if (app) {
app.globalData.userInfo = updatedUserInfo;
app.globalData.isLoggedIn = true;
}
// 更新API客户端token
apiClient.setToken(response.data.access_token);
console.log('Token刷新成功');
return true;
} else {
throw new Error('刷新token响应格式错误');
}
} catch (error) {
console.error('Token刷新失败:', error);
this.clearAuthData();
return false;
}
}
// 登录成功后保存认证信息
async saveAuthData(loginData) {
try {
console.log('开始保存认证信息:', {
hasAccessToken: !!loginData.access_token,
hasRefreshToken: !!loginData.refresh_token,
hasUser: !!loginData.user,
userCustomId: loginData.user?.customId,
expiresAt: loginData.expires_at
});
// 先清除旧的认证数据
this.clearAuthData();
const userInfo = {
token: loginData.access_token,
refreshToken: loginData.refresh_token,
user: loginData.user,
expiresAt: loginData.expires_at * 1000, // 转换为毫秒时间戳
loginTime: Date.now()
};
// 保存到本地存储
wx.setStorageSync('userInfo', userInfo);
// 更新全局状态
const app = this.getApp();
if (app) {
app.globalData.userInfo = userInfo;
app.globalData.isLoggedIn = true;
}
// 设置API客户端token
apiClient.setToken(userInfo.token);
console.log('认证信息保存成功用户customId:', userInfo.user?.customId);
// 验证保存是否成功
const savedInfo = wx.getStorageSync('userInfo');
console.log('验证保存结果:', {
saved: !!savedInfo,
customId: savedInfo?.user?.customId,
hasToken: !!savedInfo?.token
});
return true;
} catch (error) {
console.error('保存认证信息失败:', error);
return false;
}
}
// 清除认证数据
clearAuthData() {
try {
// 清除本地存储
wx.removeStorageSync('userInfo');
// 清除全局状态
const app = this.getApp();
if (app) {
app.globalData.userInfo = null;
app.globalData.isLoggedIn = false;
}
// 清除API客户端token
apiClient.clearToken();
console.log('认证数据已清除');
} catch (error) {
console.error('清除认证数据失败:', error);
}
}
// 登出
async logout() {
try {
console.log('开始登出');
// 调用登出API
try {
await apiClient.logout();
console.log('服务端登出成功');
} catch (error) {
console.error('服务端登出失败,继续本地清理:', error);
}
// 清除本地认证数据
this.clearAuthData();
console.log('登出完成');
return true;
} catch (error) {
console.error('登出失败:', error);
// 即使失败也要清除本地数据
this.clearAuthData();
return false;
}
}
// 检查并跳转到登录页
requireAuth() {
if (!this.isLoggedIn()) {
console.log('需要登录,跳转到登录页');
wx.reLaunch({
url: '/pages/login/login'
});
return false;
}
return true;
}
// 静默登录(小程序启动时调用)
async silentLogin() {
// 防止并发登录
if (this.loginPromise) {
return await this.loginPromise;
}
console.log('开始静默登录');
this.loginPromise = this._doSilentLogin();
try {
const result = await this.loginPromise;
return result;
} finally {
this.loginPromise = null;
}
}
// 执行静默登录
async _doSilentLogin() {
try {
// 先检查本地状态
if (!this.isInitialized) {
await this.init();
}
// 如果已经登录且token有效
const app = this.getApp();
const userInfo = app?.globalData?.userInfo;
if (this.isLoggedIn() && this.isTokenValid(userInfo)) {
console.log('当前登录状态有效,无需静默登录');
return true;
}
// 尝试刷新token
const storedUserInfo = userInfo || wx.getStorageSync('userInfo');
if (storedUserInfo?.refreshToken) {
console.log('尝试使用refresh token恢复登录');
const refreshResult = await this.refreshTokenIfNeeded(storedUserInfo);
if (refreshResult) {
console.log('静默登录成功通过token刷新');
return true;
}
}
console.log('静默登录失败,需要用户手动登录');
return false;
} catch (error) {
console.error('静默登录异常:', error);
this.clearAuthData();
return false;
}
}
// 获取用户基本信息(用于页面显示)
getUserDisplayInfo() {
const user = this.getCurrentUser();
if (!user) {
return null;
}
return {
nickname: user.nickname || user.username || '未知用户',
avatar: user.avatar || '',
phone: user.phone || '',
customId: user.customId || user.custom_id || ''
};
}
// 检查特定权限
hasPermission(permission) {
const user = this.getCurrentUser();
if (!user) {
return false;
}
// 这里可以根据实际需求检查用户权限
// 例如user.permissions?.includes(permission)
return true;
}
// 等待认证初始化完成
async waitForInitialization() {
if (this.isInitialized) {
return;
}
return new Promise((resolve) => {
const checkInterval = setInterval(() => {
if (this.isInitialized) {
clearInterval(checkInterval);
resolve();
}
}, 100);
});
}
// 🔥 调试方法:检查当前认证状态
debugAuthStatus() {
try {
const app = this.getApp();
const globalUserInfo = app?.globalData?.userInfo;
const storedUserInfo = wx.getStorageSync('userInfo');
const apiToken = apiClient.getToken();
console.log('=== 认证状态调试信息 ===');
console.log('1. 全局状态:', {
isLoggedIn: app?.globalData?.isLoggedIn,
hasGlobalUserInfo: !!globalUserInfo,
globalCustomId: globalUserInfo?.user?.customId,
globalToken: globalUserInfo?.token ? globalUserInfo.token.substring(0, 20) + '...' : null
});
console.log('2. 本地存储:', {
hasStoredUserInfo: !!storedUserInfo,
storedCustomId: storedUserInfo?.user?.customId,
storedToken: storedUserInfo?.token ? storedUserInfo.token.substring(0, 20) + '...' : null
});
console.log('3. API客户端:', {
hasApiToken: !!apiToken,
apiToken: apiToken ? apiToken.substring(0, 20) + '...' : null
});
console.log('4. 一致性检查:', {
globalVsStored: globalUserInfo?.user?.customId === storedUserInfo?.user?.customId,
globalVsApi: globalUserInfo?.token === apiToken,
storedVsApi: storedUserInfo?.token === apiToken
});
console.log('========================');
} catch (error) {
console.error('调试认证状态失败:', error);
}
}
}
// 创建全局实例
const authManager = new AuthManager();
// 导出认证管理器
module.exports = authManager;