jwt.util.ts 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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; tenantId?: number }, 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. tenantId: user.tenantId || undefined,
  28. ...additionalPayload
  29. };
  30. try {
  31. const options: SignOptions = {
  32. expiresIn: (expiresIn || JWT_EXPIRES_IN) as SignOptions['expiresIn'] | number
  33. };
  34. return jwt.sign(payload, JWT_SECRET, options);
  35. } catch (error) {
  36. logger.error('生成JWT token失败:', error);
  37. throw new Error('生成token失败');
  38. }
  39. }
  40. /**
  41. * 验证 JWT token
  42. * @param token JWT token
  43. * @returns 验证后的 payload
  44. */
  45. static verifyToken(token: string): JWTPayload {
  46. try {
  47. return jwt.verify(token, JWT_SECRET) as JWTPayload;
  48. } catch (error) {
  49. logger.error('验证JWT token失败:', error);
  50. throw new Error('无效的token');
  51. }
  52. }
  53. /**
  54. * 解码 JWT token(不验证签名)
  55. * @param token JWT token
  56. * @returns 解码后的 payload
  57. */
  58. static decodeToken(token: string): JWTPayload | null {
  59. try {
  60. return jwt.decode(token) as JWTPayload;
  61. } catch (error) {
  62. logger.error('解码JWT token失败:', error);
  63. return null;
  64. }
  65. }
  66. /**
  67. * 获取 token 的剩余有效期(秒)
  68. * @param token JWT token
  69. * @returns 剩余有效期(秒),如果 token 无效则返回 0
  70. */
  71. static getTokenRemainingTime(token: string): number {
  72. try {
  73. const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload & { exp: number };
  74. const currentTime = Math.floor(Date.now() / 1000);
  75. return Math.max(0, decoded.exp - currentTime);
  76. } catch (error) {
  77. return 0;
  78. }
  79. }
  80. }