auth.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { Request, Response, NextFunction } from 'express';
  2. import jwt from 'jsonwebtoken';
  3. import { config } from '../config/index.js';
  4. import { AppError } from './error.js';
  5. import { ERROR_CODES, HTTP_STATUS } from '@media-manager/shared';
  6. import type { User, UserRole } from '@media-manager/shared';
  7. export interface JwtPayload {
  8. userId: number;
  9. username: string;
  10. role: UserRole;
  11. }
  12. declare global {
  13. namespace Express {
  14. interface Request {
  15. user?: JwtPayload;
  16. }
  17. }
  18. }
  19. /**
  20. * JWT 认证中间件
  21. */
  22. export function authenticate(req: Request, _res: Response, next: NextFunction): void {
  23. const authHeader = req.headers.authorization;
  24. if (!authHeader || !authHeader.startsWith('Bearer ')) {
  25. throw new AppError('未提供认证令牌', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.UNAUTHORIZED);
  26. }
  27. const token = authHeader.substring(7);
  28. try {
  29. const decoded = jwt.verify(token, config.jwt.secret) as JwtPayload;
  30. req.user = decoded;
  31. next();
  32. } catch (error) {
  33. if (error instanceof jwt.TokenExpiredError) {
  34. throw new AppError('认证令牌已过期', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_EXPIRED);
  35. }
  36. throw new AppError('无效的认证令牌', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_INVALID);
  37. }
  38. }
  39. /**
  40. * 角色授权中间件
  41. */
  42. export function authorize(...roles: UserRole[]) {
  43. return (req: Request, _res: Response, next: NextFunction): void => {
  44. if (!req.user) {
  45. throw new AppError('未认证', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.UNAUTHORIZED);
  46. }
  47. if (!roles.includes(req.user.role)) {
  48. throw new AppError('没有权限执行此操作', HTTP_STATUS.FORBIDDEN, ERROR_CODES.FORBIDDEN);
  49. }
  50. next();
  51. };
  52. }
  53. /**
  54. * 生成访问令牌
  55. */
  56. export function generateAccessToken(payload: JwtPayload): string {
  57. return jwt.sign(payload, config.jwt.secret, {
  58. expiresIn: config.jwt.accessExpiresIn,
  59. });
  60. }
  61. /**
  62. * 生成刷新令牌
  63. */
  64. export function generateRefreshToken(payload: JwtPayload): string {
  65. return jwt.sign(payload, config.jwt.secret, {
  66. expiresIn: config.jwt.refreshExpiresIn,
  67. });
  68. }
  69. /**
  70. * 验证刷新令牌
  71. */
  72. export function verifyRefreshToken(token: string): JwtPayload {
  73. try {
  74. return jwt.verify(token, config.jwt.secret) as JwtPayload;
  75. } catch (error) {
  76. if (error instanceof jwt.TokenExpiredError) {
  77. throw new AppError('刷新令牌已过期', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_EXPIRED);
  78. }
  79. throw new AppError('无效的刷新令牌', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_INVALID);
  80. }
  81. }