import jwt, { SignOptions } from 'jsonwebtoken'; import { JWTPayload } from '@d8d/shared-types'; import debug from 'debug'; const logger = { info: debug('backend:jwt:info'), error: debug('backend:jwt:error') }; const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d'; export class JWTUtil { /** * 生成 JWT token * @param user 用户实体 * @param additionalPayload 额外的 payload 数据 * @param expiresIn 过期时间 * @returns JWT token */ static generateToken(user: { id: number; username: string; roles?: { name: string }[]; openid?: string }, additionalPayload: Partial = {}, expiresIn?: string): string { if (!user.id || !user.username) { throw new Error('用户ID和用户名不能为空'); } const payload: JWTPayload = { id: user.id, username: user.username, roles: user.roles?.map(role => role.name) || [], openid: user.openid || undefined, ...additionalPayload }; try { const options: SignOptions = { expiresIn: expiresIn || JWT_EXPIRES_IN }; return jwt.sign(payload, JWT_SECRET, options); } catch (error) { logger.error('生成JWT token失败:', error); throw new Error('生成token失败'); } } /** * 验证 JWT token * @param token JWT token * @returns 验证后的 payload */ static verifyToken(token: string): JWTPayload { try { return jwt.verify(token, JWT_SECRET) as JWTPayload; } catch (error) { logger.error('验证JWT token失败:', error); throw new Error('无效的token'); } } /** * 解码 JWT token(不验证签名) * @param token JWT token * @returns 解码后的 payload */ static decodeToken(token: string): JWTPayload | null { try { return jwt.decode(token) as JWTPayload; } catch (error) { logger.error('解码JWT token失败:', error); return null; } } /** * 获取 token 的剩余有效期(秒) * @param token JWT token * @returns 剩余有效期(秒),如果 token 无效则返回 0 */ static getTokenRemainingTime(token: string): number { try { const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload & { exp: number }; const currentTime = Math.floor(Date.now() / 1000); return Math.max(0, decoded.exp - currentTime); } catch (error) { return 0; } } }