/** * JWT 工具函数 */ /** * JWT Payload 接口 */ export interface JwtPayload { sub?: string; // subject (通常是 userId) userId?: number; // 用户ID (驼峰命名) user_id?: number; // 用户ID (下划线命名) exp?: number; // 过期时间 iat?: number; // 签发时间 [key: string]: unknown; } /** * 解析 JWT token 的 payload * @param token JWT token * @returns 解析后的 payload,解析失败返回 null */ export function parseJwtPayload(token: string): JwtPayload | null { try { const parts = token.split('.'); if (parts.length !== 3) { console.warn('[JWT] token 格式不正确,期望3部分,实际:', parts.length); return null; } // payload 是第二部分,base64url 编码 const payload = parts[1]; // base64url 转 base64 const base64 = payload.replace(/-/g, '+').replace(/_/g, '/'); // 解码 const jsonStr = Buffer.from(base64, 'base64').toString('utf-8'); const parsed = JSON.parse(jsonStr); console.log('[JWT] 解析成功, payload 字段:', Object.keys(parsed)); console.log('[JWT] payload 内容:', JSON.stringify(parsed)); return parsed; } catch (error) { console.error('[JWT] 解析失败:', error); return null; } } /** * 从 JWT token 中获取用户ID * @param token JWT token * @returns 用户ID字符串,获取失败返回 null */ export function getUserIdFromToken(token: string): string | null { const payload = parseJwtPayload(token); if (!payload) { return null; } // 支持多种字段名:user_id, userId, sub if (payload.user_id !== undefined) { return String(payload.user_id); } if (payload.userId !== undefined) { return String(payload.userId); } if (payload.sub !== undefined) { return String(payload.sub); } console.warn('[JWT] payload 中没有 user_id, userId 或 sub 字段'); return null; } /** * 检测 JWT token 是否已过期 * @param token JWT token * @param bufferSeconds 提前多少秒判定为过期(默认60秒) * @returns true 表示已过期,false 表示未过期,null 表示无法判断 */ export function isTokenExpired(token: string, bufferSeconds: number = 60): boolean | null { const payload = parseJwtPayload(token); if (!payload) { return null; } if (payload.exp === undefined) { console.warn('[JWT] payload 中没有 exp 字段,无法判断过期'); return null; } const now = Math.floor(Date.now() / 1000); const expTime = payload.exp - bufferSeconds; const isExpired = now >= expTime; if (isExpired) { console.warn('[JWT] token 已过期,exp:', payload.exp, '当前:', now); } return isExpired; }