import { useUserStore } from "@/stores/modules/user"; // JWT Token解析后的载荷接口 export interface JwtPayload { userId?: string; loginName?: string; exp?: number; iat?: number; [key: string]: any; } // Token信息摘要接口 export interface TokenInfo { userId: string | null; loginName: string | null; expiration: string | null; isExpired: boolean; remainingTime: string; } /** * JWT工具类 * 提供JWT token的解析、验证等功能 */ export class JwtUtil { /** * Base64URL解码 * @param str Base64URL编码的字符串 */ private static base64UrlDecode(str: string): string { try { // Base64URL转Base64 let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); // 补齐padding while (base64.length % 4) { base64 += '='; } // Base64解码 const decoded = atob(base64); // 处理UTF-8编码 return decodeURIComponent( decoded .split('') .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) .join('') ); } catch (error) { throw new Error('Base64URL解码失败: ' + error); } } /** * 解析JWT Token获取载荷信息 * @param token JWT token字符串,如果不传则从store中获取 */ static parseToken(token?: string): JwtPayload | null { try { let targetToken = token; // 如果没有传入token,从store中获取 if (!targetToken) { const userStore = useUserStore(); targetToken = userStore.accessToken; } if (!targetToken) { console.warn('Token不存在'); return null; } // JWT token由三部分组成,用.分割:header.payload.signature const parts = targetToken.split('.'); if (parts.length !== 3) { console.error('无效的JWT token格式'); return null; } // 解码payload部分(第二部分) const payload = parts[1]; const decodedPayload = this.base64UrlDecode(payload); const tokenData: JwtPayload = JSON.parse(decodedPayload); return tokenData; } catch (error) { console.error('解析JWT Token失败:', error); return null; } } /** * 获取指定字段的值 * @param field 字段名 * @param token JWT token字符串,可选 */ static getField(field: string, token?: string): T | null { const tokenData = this.parseToken(token); return tokenData?.[field] || null; } /** * 获取用户ID * @param token JWT token字符串,可选 */ static getUserId(token?: string): string | null { return this.getField('userId', token); } /** * 获取登录名 * @param token JWT token字符串,可选 */ static getLoginName(token?: string): string | null { return this.getField('loginName', token); } /** * 获取Token过期时间戳(秒) * @param token JWT token字符串,可选 */ static getExpiration(token?: string): number | null { return this.getField('exp', token); } /** * 获取Token签发时间戳(秒) * @param token JWT token字符串,可选 */ static getIssuedAt(token?: string): number | null { return this.getField('iat', token); } /** * 检查Token是否过期 * @param token JWT token字符串,可选 */ static isExpired(token?: string): boolean { const exp = this.getExpiration(token); if (!exp) return true; // JWT中的exp是秒级时间戳,需要转换为毫秒 const expTime = exp * 1000; return Date.now() >= expTime; } /** * 获取Token剩余有效时间(毫秒) * @param token JWT token字符串,可选 */ static getRemainingTime(token?: string): number { const exp = this.getExpiration(token); if (!exp) return 0; const expTime = exp * 1000; const remaining = expTime - Date.now(); return Math.max(0, remaining); } /** * 格式化剩余时间为可读字符串 * @param ms 毫秒数 */ static formatRemainingTime(ms: number): string { if (ms <= 0) return '已过期'; const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) return `${days}天 ${hours % 24}小时`; if (hours > 0) return `${hours}小时 ${minutes % 60}分钟`; if (minutes > 0) return `${minutes}分钟 ${seconds % 60}秒`; return `${seconds}秒`; } /** * 获取完整的Token信息摘要 * @param token JWT token字符串,可选 */ static getTokenInfo(token?: string): TokenInfo { const exp = this.getExpiration(token); const remainingMs = this.getRemainingTime(token); return { userId: this.getUserId(token), loginName: this.getLoginName(token), expiration: exp ? new Date(exp * 1000).toLocaleString() : null, isExpired: this.isExpired(token), remainingTime: this.formatRemainingTime(remainingMs) }; } /** * 验证Token是否有效(格式正确且未过期) * @param token JWT token字符串,可选 */ static isValid(token?: string): boolean { const tokenData = this.parseToken(token); return tokenData !== null && !this.isExpired(token); } /** * 获取Token的头部信息 * @param token JWT token字符串,可选 */ static getHeader(token?: string): any | null { try { let targetToken = token; if (!targetToken) { const userStore = useUserStore(); targetToken = userStore.accessToken; } if (!targetToken) return null; const parts = targetToken.split('.'); if (parts.length !== 3) return null; const header = parts[0]; const decodedHeader = this.base64UrlDecode(header); return JSON.parse(decodedHeader); } catch (error) { console.error('解析JWT Header失败:', error); return null; } } } // 导出单例方法,方便直接调用 export const jwtUtil = JwtUtil;