import jwt from 'jsonwebtoken' // 简化的JWT载荷接口 export interface JWTPayload { userId: number role: string jti: string // JWT ID,用于唯一标识token iat?: number exp?: number } /** * 简化的JWT工具类 - 仅支持单一JWT token验证 */ export class JWTEnhanced { private static readonly JWT_SECRET = process.env.JWT_SECRET as string private static readonly TOKEN_EXPIRES = '7d' // 7天有效期 private static readonly REFRESH_THRESHOLD = 24 * 60 * 60 // 24小时(秒),剩余时间少于此值时自动续期 /** * 通用签名方法 */ static sign(payload: object, options?: jwt.SignOptions): string { if (!this.JWT_SECRET) { throw new Error('JWT_SECRET environment variable is not set') } return jwt.sign(payload, this.JWT_SECRET, options) } /** * 通用验证方法 */ static verify(token: string): any { if (!this.JWT_SECRET) { throw new Error('JWT_SECRET environment variable is not set') } return jwt.verify(token, this.JWT_SECRET) } /** * 生成JWT token(返回 token 字符串,兼容旧调用) */ static generateToken(userId: number, role: string): string { return this.generateTokenWithJti(userId, role).token } /** * 生成JWT token 并返回 jti(会话表关联需要) */ static generateTokenWithJti(userId: number, role: string): { token: string; jti: string } { if (!this.JWT_SECRET) { throw new Error('JWT_SECRET environment variable is not set') } const jti = `${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` const payload: Omit = { userId, role, jti } const token = jwt.sign(payload, this.JWT_SECRET, { expiresIn: this.TOKEN_EXPIRES }) console.log(`Generated JWT token for user ${userId}`) return { token, jti } } /** * 验证并解码token */ static verifyToken(token: string): JWTPayload { if (!this.JWT_SECRET) { throw new Error('JWT_SECRET environment variable is not set') } try { // 验证并解码token const decoded = jwt.verify(token, this.JWT_SECRET) as JWTPayload // 验证必要字段 if (!decoded.userId || !decoded.role || !decoded.jti) { throw new Error('Invalid token payload: missing required fields') } return decoded } catch (error: any) { if (error.name === 'TokenExpiredError') { throw new Error('Token has expired') } if (error.name === 'JsonWebTokenError') { throw new Error('Invalid token signature') } if (error.name === 'NotBeforeError') { throw new Error('Token not active yet') } throw new Error(`Token verification failed: ${error.message}`) } } /** * 获取token信息(不验证签名) */ static getTokenInfo(token: string): JWTPayload | null { try { const decoded = jwt.decode(token) as JWTPayload return decoded && decoded.userId ? decoded : null } catch (error) { return null } } /** * 检查token是否已过期 */ static isTokenExpired(token: string): boolean { const decoded = this.getTokenInfo(token) if (!decoded || !decoded.exp) return true const now = Math.floor(Date.now() / 1000) return decoded.exp <= now } /** * 获取token剩余有效时间(秒) */ static getTokenRemainingTime(token: string): number { const decoded = this.getTokenInfo(token) if (!decoded || !decoded.exp) return 0 const now = Math.floor(Date.now() / 1000) return Math.max(0, decoded.exp - now) } /** * 检查token是否需要续期 * @param token JWT token * @param thresholdSeconds 续期阈值(秒),默认24小时 * @returns 是否需要续期 */ static shouldRefreshToken( token: string, thresholdSeconds: number = this.REFRESH_THRESHOLD ): boolean { const remainingTime = this.getTokenRemainingTime(token) return remainingTime > 0 && remainingTime <= thresholdSeconds } /** * 刷新token(生成新的token) * @param oldToken 旧的token * @returns 新的token */ static refreshToken(oldToken: string): string { const decoded = this.verifyToken(oldToken) // 使用原有的用户信息生成新token return this.generateToken(decoded.userId, decoded.role) } /** * 验证token并自动续期(如果需要) * @param token JWT token * @returns { valid: boolean, payload?: JWTPayload, newToken?: string } */ static verifyAndRefresh(token: string): { valid: boolean payload?: JWTPayload newToken?: string } { try { const payload = this.verifyToken(token) // 检查是否需要续期 if (this.shouldRefreshToken(token)) { const newToken = this.refreshToken(token) console.log(`Token auto-refreshed for user ${payload.userId}`) return { valid: true, payload, newToken } } return { valid: true, payload } } catch (error) { return { valid: false } } } } // 导出默认实例 export default JWTEnhanced