init
This commit is contained in:
commit
2b8870a40e
51 changed files with 5845 additions and 0 deletions
311
utils/auth.js
Normal file
311
utils/auth.js
Normal file
|
@ -0,0 +1,311 @@
|
|||
/**
|
||||
* 认证相关工具函数
|
||||
* 包含JWT token管理、自动刷新、请求拦截等功能
|
||||
*/
|
||||
|
||||
const config = require('./config');
|
||||
const eventManager = require('./eventManager');
|
||||
|
||||
// Token刷新状态
|
||||
let isRefreshing = false;
|
||||
// 等待token刷新的请求队列
|
||||
let refreshSubscribers = [];
|
||||
|
||||
/**
|
||||
* 订阅Token刷新
|
||||
* @param {Function} callback - Token刷新后的回调函数
|
||||
*/
|
||||
const subscribeTokenRefresh = (callback) => {
|
||||
refreshSubscribers.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行Token刷新后的回调
|
||||
* @param {string} token - 新的access token
|
||||
*/
|
||||
const onTokenRefreshed = (token) => {
|
||||
refreshSubscribers.forEach(callback => callback(token));
|
||||
refreshSubscribers = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析JWT Token
|
||||
* @param {string} token - JWT token
|
||||
* @returns {Object|null} 解析后的payload或null
|
||||
*/
|
||||
const parseToken = (token) => {
|
||||
try {
|
||||
const [, payload] = token.split('.');
|
||||
return JSON.parse(atob(payload));
|
||||
} catch (error) {
|
||||
console.error('Token解析失败:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查Token是否需要刷新
|
||||
* @param {string} token - JWT token
|
||||
* @returns {boolean} 是否需要刷新
|
||||
*/
|
||||
const shouldRefreshToken = (token) => {
|
||||
const decoded = parseToken(token);
|
||||
if (!decoded || !decoded.exp) return true;
|
||||
|
||||
const expiresIn = decoded.exp * 1000 - Date.now();
|
||||
return expiresIn < config.JWT_CONFIG.refreshThreshold;
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新Token
|
||||
* @returns {Promise<string|null>} 新的access token或null
|
||||
*/
|
||||
const refreshToken = async () => {
|
||||
const refreshToken = wx.getStorageSync(config.JWT_CONFIG.storage.refresh);
|
||||
if (!refreshToken) return null;
|
||||
|
||||
try {
|
||||
const response = await wx.request({
|
||||
url: `${config.API_BASE_URL}${config.API_ENDPOINTS.AUTH.REFRESH}`,
|
||||
method: 'POST',
|
||||
header: {
|
||||
[config.JWT_CONFIG.headerKey]: `${config.JWT_CONFIG.tokenPrefix}${refreshToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.statusCode === 200 && response.data.access_token) {
|
||||
const { access_token, refresh_token } = response.data;
|
||||
wx.setStorageSync(config.JWT_CONFIG.storage.access, access_token);
|
||||
if (refresh_token) {
|
||||
wx.setStorageSync(config.JWT_CONFIG.storage.refresh, refresh_token);
|
||||
}
|
||||
return access_token;
|
||||
}
|
||||
|
||||
throw new Error(response.data?.message || '刷新Token失败');
|
||||
} catch (error) {
|
||||
console.error('刷新Token失败:', error);
|
||||
logout(); // 刷新失败时登出
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param {string} username - 用户名
|
||||
* @param {string} password - 密码
|
||||
* @returns {Promise<Object>} 登录结果,包含success和message字段
|
||||
*/
|
||||
const login = async (username, password) => {
|
||||
try {
|
||||
const response = await wx.request({
|
||||
url: `${config.API_BASE_URL}${config.API_ENDPOINTS.AUTH.LOGIN}`,
|
||||
method: 'POST',
|
||||
data: { username, password }
|
||||
});
|
||||
|
||||
if (response.statusCode === 200 && response.data.access_token) {
|
||||
const { access_token, refresh_token } = response.data;
|
||||
wx.setStorageSync(config.JWT_CONFIG.storage.access, access_token);
|
||||
wx.setStorageSync(config.JWT_CONFIG.storage.refresh, refresh_token);
|
||||
|
||||
// 触发登录成功事件
|
||||
eventManager.emit('auth:login', parseToken(access_token));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '登录成功'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: response.data?.message || '登录失败'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: '网络错误,请稍后重试'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
const logout = () => {
|
||||
// 清除所有认证信息
|
||||
wx.removeStorageSync(config.JWT_CONFIG.storage.access);
|
||||
wx.removeStorageSync(config.JWT_CONFIG.storage.refresh);
|
||||
|
||||
// 触发登出事件
|
||||
eventManager.emit('auth:logout');
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查用户是否已登录
|
||||
* @returns {boolean} 是否已登录
|
||||
*/
|
||||
const isLoggedIn = () => {
|
||||
const token = wx.getStorageSync(config.JWT_CONFIG.storage.access);
|
||||
if (!token) return false;
|
||||
|
||||
try {
|
||||
// 解析JWT token(不验证签名)
|
||||
const [, payload] = token.split('.');
|
||||
const { exp } = JSON.parse(atob(payload));
|
||||
|
||||
// 检查token是否已过期
|
||||
return Date.now() < exp * 1000;
|
||||
} catch (error) {
|
||||
console.error('Token解析失败:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
* @returns {Object|null} 用户信息或null
|
||||
*/
|
||||
const getCurrentUser = () => {
|
||||
const token = wx.getStorageSync(config.JWT_CONFIG.storage.access);
|
||||
if (!token) return null;
|
||||
|
||||
try {
|
||||
// 解析JWT token(不验证签名)
|
||||
const [, payload] = token.split('.');
|
||||
const decoded = JSON.parse(atob(payload));
|
||||
|
||||
return {
|
||||
id: decoded.sub,
|
||||
username: decoded.username,
|
||||
// 其他用户信息...
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取访问令牌
|
||||
* @param {boolean} autoRefresh - 是否自动刷新
|
||||
* @returns {Promise<string|null>} 访问令牌或null
|
||||
*/
|
||||
const getAccessToken = async (autoRefresh = true) => {
|
||||
const token = wx.getStorageSync(config.JWT_CONFIG.storage.access);
|
||||
if (!token) return null;
|
||||
|
||||
// 检查是否需要刷新token
|
||||
if (autoRefresh && shouldRefreshToken(token)) {
|
||||
if (isRefreshing) {
|
||||
// 如果正在刷新,返回一个Promise等待刷新完成
|
||||
return new Promise(resolve => {
|
||||
subscribeTokenRefresh(newToken => {
|
||||
resolve(newToken);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
isRefreshing = true;
|
||||
const newToken = await refreshToken();
|
||||
isRefreshing = false;
|
||||
|
||||
if (newToken) {
|
||||
onTokenRefreshed(newToken);
|
||||
return newToken;
|
||||
}
|
||||
|
||||
// 刷新失败,清除token
|
||||
logout();
|
||||
return null;
|
||||
}
|
||||
|
||||
return token;
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建请求拦截器
|
||||
* @returns {Function} 请求拦截器函数
|
||||
*/
|
||||
const createRequestInterceptor = () => {
|
||||
const originalRequest = wx.request;
|
||||
|
||||
// 重写wx.request方法
|
||||
wx.request = function(options) {
|
||||
const { url, header = {}, ...restOptions } = options;
|
||||
|
||||
// 判断是否需要添加认证头
|
||||
const isAuthUrl = url.includes(config.API_BASE_URL) &&
|
||||
!url.includes(config.API_ENDPOINTS.AUTH.LOGIN);
|
||||
|
||||
if (isAuthUrl) {
|
||||
// 创建一个Promise来处理认证
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const token = await getAccessToken();
|
||||
if (!token) {
|
||||
// 没有token且需要认证,触发未授权事件
|
||||
eventManager.emit('auth:unauthorized');
|
||||
reject(new Error('未授权,请先登录'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加认证头
|
||||
const authHeader = {
|
||||
...header,
|
||||
[config.JWT_CONFIG.headerKey]: `${config.JWT_CONFIG.tokenPrefix}${token}`
|
||||
};
|
||||
|
||||
// 发送请求
|
||||
originalRequest({
|
||||
...restOptions,
|
||||
url,
|
||||
header: authHeader,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 不需要认证的请求直接发送
|
||||
return originalRequest(options);
|
||||
};
|
||||
|
||||
return () => {
|
||||
// 恢复原始请求方法
|
||||
wx.request = originalRequest;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化认证模块
|
||||
*/
|
||||
const initAuth = () => {
|
||||
createRequestInterceptor();
|
||||
|
||||
// 监听网络状态变化
|
||||
wx.onNetworkStatusChange(function(res) {
|
||||
if (res.isConnected && isLoggedIn()) {
|
||||
// 网络恢复且已登录,尝试刷新token
|
||||
getAccessToken();
|
||||
}
|
||||
});
|
||||
|
||||
console.log('认证模块初始化完成');
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
login,
|
||||
logout,
|
||||
isLoggedIn,
|
||||
getCurrentUser,
|
||||
getAccessToken,
|
||||
initAuth,
|
||||
parseToken,
|
||||
shouldRefreshToken
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue