jwt.util.ts 2.5 KB

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