jwt.util.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import jwt, { SignOptions } from 'jsonwebtoken';
  2. import { UserEntity } from '../modules/users/user.entity';
  3. import debug from 'debug';
  4. const logger = {
  5. info: debug('backend:jwt:info'),
  6. error: debug('backend:jwt:error')
  7. };
  8. const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
  9. const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
  10. export interface JWTPayload {
  11. id: number;
  12. username: string;
  13. roles?: string[];
  14. openid?: string;
  15. }
  16. export class JWTUtil {
  17. /**
  18. * 生成 JWT token
  19. * @param user 用户实体
  20. * @param additionalPayload 额外的 payload 数据
  21. * @returns JWT token
  22. */
  23. static generateToken(user: UserEntity, additionalPayload: Partial<JWTPayload> = {}): string {
  24. if (!user.id || !user.username) {
  25. throw new Error('用户ID和用户名不能为空');
  26. }
  27. const payload: JWTPayload = {
  28. id: user.id,
  29. username: user.username,
  30. roles: user.roles?.map(role => role.name) || [],
  31. openid: user.openid || undefined,
  32. ...additionalPayload
  33. };
  34. try {
  35. return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN as SignOptions['expiresIn']});
  36. } catch (error) {
  37. logger.error('生成JWT token失败:', error);
  38. throw new Error('生成token失败');
  39. }
  40. }
  41. /**
  42. * 验证 JWT token
  43. * @param token JWT token
  44. * @returns 验证后的 payload
  45. */
  46. static verifyToken(token: string): JWTPayload {
  47. try {
  48. return jwt.verify(token, JWT_SECRET) as JWTPayload;
  49. } catch (error) {
  50. logger.error('验证JWT token失败:', error);
  51. throw new Error('无效的token');
  52. }
  53. }
  54. /**
  55. * 解码 JWT token(不验证签名)
  56. * @param token JWT token
  57. * @returns 解码后的 payload
  58. */
  59. static decodeToken(token: string): JWTPayload | null {
  60. try {
  61. return jwt.decode(token) as JWTPayload;
  62. } catch (error) {
  63. logger.error('解码JWT token失败:', error);
  64. return null;
  65. }
  66. }
  67. /**
  68. * 获取 token 的剩余有效期(秒)
  69. * @param token JWT token
  70. * @returns 剩余有效期(秒),如果 token 无效则返回 0
  71. */
  72. static getTokenRemainingTime(token: string): number {
  73. try {
  74. const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload & { exp: number };
  75. const currentTime = Math.floor(Date.now() / 1000);
  76. return Math.max(0, decoded.exp - currentTime);
  77. } catch (error) {
  78. return 0;
  79. }
  80. }
  81. }