upload project
This commit is contained in:
commit
06961cae04
422 changed files with 110626 additions and 0 deletions
443
utils/auth.js
Normal file
443
utils/auth.js
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
// 认证工具类 - 参考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;
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue