findme-miniprogram-frontend/utils/auth.js
2025-12-27 17:16:03 +08:00

443 lines
11 KiB
JavaScript
Raw Permalink 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() {
// 实现单例模式,防止重复创建实例
if (AuthManager._instance) {
return AuthManager._instance;
}
this.isInitialized = false;
this.loginPromise = null; // 防止并发登录
this.tokenRefreshPromise = null; // 防止并发刷新token
this._app = null; // 缓存app实例
AuthManager._instance = this;
}
// 安全获取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;
}
try {
// 检查本地存储的用户信息
const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.token) {
// 检查token是否过期
if (this.isTokenValid(userInfo)) {
// 恢复登录状态
const app = this.getApp();
if (app) {
app.globalData.userInfo = userInfo;
app.globalData.isLoggedIn = true;
}
apiClient.setToken(userInfo.token);
} else {
await this.refreshTokenIfNeeded(userInfo);
}
} else {
}
this.isInitialized = true;
} 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()) {
return true;
}
return false;
}
// 刷新token
async refreshTokenIfNeeded(userInfo) {
// 防止并发刷新
if (this.tokenRefreshPromise) {
return await this.tokenRefreshPromise;
}
if (!userInfo?.refreshToken) {
this.clearAuthData();
return false;
}
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);
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,
hasNeteaseIMAccid: !!loginData.neteaseIMAccid,
hasNeteaseIMToken: !!loginData.neteaseIMToken
});
// 先清除旧的认证数据
this.clearAuthData();
const userInfo = {
token: loginData.access_token,
refreshToken: loginData.refresh_token,
user: loginData.user,
expiresAt: loginData.expires_at * 1000, // 转换为毫秒时间戳
loginTime: Date.now(),
// 🔥 保存 NIM 账号和密码
neteaseIMAccid: loginData.neteaseIMAccid,
neteaseIMToken: loginData.neteaseIMToken
};
// 保存到本地存储
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);
console.log('NIM账号保存成功:', userInfo.neteaseIMAccid);
// 验证保存是否成功
const savedInfo = wx.getStorageSync('userInfo');
console.log('验证保存结果:', {
saved: !!savedInfo,
customId: savedInfo?.user?.customId,
hasToken: !!savedInfo?.token,
hasNeteaseIMAccid: !!savedInfo?.neteaseIMAccid,
hasNeteaseIMToken: !!savedInfo?.neteaseIMToken
});
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();
} catch (error) {
console.error('清除认证数据失败:', error);
}
}
// 登出
async logout() {
try {
// 调用登出API
try {
await apiClient.logout();
} catch (error) {
console.error('服务端登出失败,继续本地清理:', error);
}
// 清除本地认证数据
this.clearAuthData();
return true;
} catch (error) {
console.error('登出失败:', error);
// 即使失败也要清除本地数据
this.clearAuthData();
return false;
}
}
// 检查登录状态
// autoRedirect: 可选是否在未登录时自动跳转登录页默认false
requireAuth(autoRedirect = false) {
if (!this.isLoggedIn()) {
if (autoRedirect) {
wx.reLaunch({
url: '/pages/login/login'
});
}
return false;
}
return true;
}
// 静默登录(小程序启动时调用)
async silentLogin() {
// 防止并发登录
if (this.loginPromise) {
return await this.loginPromise;
}
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)) {
return true;
}
// 尝试刷新token
const storedUserInfo = userInfo || wx.getStorageSync('userInfo');
if (storedUserInfo?.refreshToken) {
const refreshResult = await this.refreshTokenIfNeeded(storedUserInfo);
if (refreshResult) {
return true;
}
}
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('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
});
} catch (error) {
console.error('调试认证状态失败:', error);
}
}
}
// 创建全局实例
const authManager = new AuthManager();
// 导出认证管理器
module.exports = authManager;