findme-miniprogram-frontend/utils/auth.js

444 lines
11 KiB
JavaScript
Raw Permalink Normal View History

2025-12-27 17:16:03 +08:00
// 认证工具类 - 参考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;