jwt.util.ts 2.4 KB

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