// 认证模块 - 处理token管理和API调用封装 import { t } from './i18n.js'; /** * 认证管理类 */ class AuthManager { constructor() { this.tokenKey = 'authToken'; this.expiryKey = 'authTokenExpiry'; this.baseURL = window.location.origin; } /** * 获取存储的token */ getToken() { return localStorage.getItem(this.tokenKey); } /** * 获取token过期时间 */ getTokenExpiry() { const expiry = localStorage.getItem(this.expiryKey); return expiry ? parseInt(expiry) : null; } /** * 检查token是否有效 */ isTokenValid() { const token = this.getToken(); const expiry = this.getTokenExpiry(); if (!token) return false; // 如果设置了过期时间,检查是否过期 if (expiry && Date.now() > expiry) { this.clearToken(); return false; } return true; } /** * 保存token到本地存储 */ saveToken(token, rememberMe = false) { localStorage.setItem(this.tokenKey, token); if (rememberMe) { const expiryTime = Date.now() + (7 * 24 * 60 * 60 * 1000); // 7天 localStorage.setItem(this.expiryKey, expiryTime.toString()); } } /** * 清除token */ clearToken() { localStorage.removeItem(this.tokenKey); localStorage.removeItem(this.expiryKey); } /** * 登出 */ async logout() { this.clearToken(); window.location.href = '/login.html'; } } /** * API调用封装类 */ class ApiClient { constructor() { this.authManager = new AuthManager(); this.baseURL = window.location.origin; } /** * 获取带认证的请求头 */ getAuthHeaders() { const token = this.authManager.getToken(); return token ? { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } : { 'Content-Type': 'application/json' }; } /** * 处理401错误重定向到登录页 */ handleUnauthorized() { this.authManager.clearToken(); window.location.href = '/login.html'; } /** * 通用API请求方法 */ async request(endpoint, options = {}) { const url = `${this.baseURL}/api${endpoint}`; const headers = { ...this.getAuthHeaders(), ...options.headers }; const config = { ...options, headers }; try { const response = await fetch(url, config); // 如果是401错误,重定向到登录页 if (response.status === 401) { this.handleUnauthorized(); throw new Error(t('common.unauthorized')); } const contentType = response.headers.get('content-type'); let data; if (contentType && contentType.includes('application/json')) { data = await response.json(); } else { data = await response.text(); } // 如果响应状态码不是 2xx,抛出错误 if (!response.ok) { let errorMessage; if (data && typeof data === 'object') { // 优先使用错误代码进行翻译 const code = (data.error && data.error.messageCode) || data.messageCode; if (code) { const translated = t(code); if (translated !== code) { errorMessage = translated; } } // 如果没有翻译,使用原始错误消息 if (!errorMessage) { errorMessage = (data.error && data.error.message) || data.message; } } if (!errorMessage) { errorMessage = `${t('common.requestFailed')} (${t('common.status')}: ${response.status})`; } throw new Error(errorMessage); } return data; } catch (error) { if (error.message === t('common.unauthorized')) { // 已经在handleUnauthorized中处理了重定向 throw error; } console.error('API请求错误:', error); throw error; } } /** * GET请求 */ async get(endpoint, params = {}) { const queryString = new URLSearchParams(params).toString(); const url = queryString ? `${endpoint}?${queryString}` : endpoint; return this.request(url, { method: 'GET' }); } /** * POST请求 */ async post(endpoint, data = {}) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data) }); } /** * PUT请求 */ async put(endpoint, data = {}) { return this.request(endpoint, { method: 'PUT', body: JSON.stringify(data) }); } /** * DELETE请求 */ async delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); } /** * POST请求(支持FormData上传) */ async upload(endpoint, formData) { const url = `${this.baseURL}/api${endpoint}`; // 获取认证token const token = this.authManager.getToken(); const headers = {}; // 如果有token,添加Authorization头部 if (token) { headers['Authorization'] = `Bearer ${token}`; } // 对于FormData请求,不添加Content-Type头部,让浏览器自动设置 const config = { method: 'POST', headers, body: formData }; try { const response = await fetch(url, config); // 如果是401错误,重定向到登录页 if (response.status === 401) { this.handleUnauthorized(); throw new Error(t('common.unauthorized')); } const contentType = response.headers.get('content-type'); let data; if (contentType && contentType.includes('application/json')) { data = await response.json(); } else { data = await response.text(); } // 如果响应状态码不是 2xx,抛出错误 if (!response.ok) { const errorMessage = (data && typeof data === 'object' && data.error && data.error.message) || (data && typeof data === 'object' && data.message) || `${t('common.uploadFailed')} (${t('common.status')}: ${response.status})`; throw new Error(errorMessage); } return data; } catch (error) { if (error.message === t('common.unauthorized')) { // 已经在handleUnauthorized中处理了重定向 throw error; } console.error('API请求错误:', error); throw error; } } } /** * 初始化认证检查 */ async function initAuth() { const authManager = new AuthManager(); // 检查是否已经有有效的token if (authManager.isTokenValid()) { // 验证token是否仍然有效(发送一个测试请求) try { const apiClient = new ApiClient(); await apiClient.get('/health'); return true; } catch (error) { // Token无效,清除并重定向到登录页 authManager.clearToken(); window.location.href = '/login.html'; return false; } } else { // 没有有效token,重定向到登录页 window.location.href = '/login.html'; return false; } } /** * 登出函数 */ async function logout() { const authManager = new AuthManager(); await authManager.logout(); } /** * 登录函数(供登录页面使用) */ async function login(password, rememberMe = false) { try { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password, rememberMe }) }); const data = await response.json(); if (data.success) { // 保存token const authManager = new AuthManager(); authManager.saveToken(data.token, rememberMe); return { success: true }; } else { return { success: false, message: data.message }; } } catch (error) { console.error('登录错误:', error); return { success: false, message: '登录失败,请检查网络连接' }; } } // 创建单例实例 const authManager = new AuthManager(); const apiClient = new ApiClient(); /** * 获取带认证的请求头(便捷函数) * @returns {Object} 包含认证信息的请求头 */ function getAuthHeaders() { return apiClient.getAuthHeaders(); } // 导出实例到 window(兼容旧代码) window.authManager = authManager; window.apiClient = apiClient; window.initAuth = initAuth; window.logout = logout; window.login = login; // 导出认证管理器类和API客户端类供其他模块使用 window.AuthManager = AuthManager; window.ApiClient = ApiClient; // ES6 模块导出 export { AuthManager, ApiClient, authManager, apiClient, initAuth, logout, login, getAuthHeaders }; console.log('认证模块已加载');