import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; import { config } from '../config/index.js'; import { AppError } from './error.js'; import { ERROR_CODES, HTTP_STATUS } from '@media-manager/shared'; import type { User, UserRole } from '@media-manager/shared'; export interface JwtPayload { userId: number; username: string; role: UserRole; } declare global { namespace Express { interface Request { user?: JwtPayload; } } } /** * JWT 认证中间件 */ export function authenticate(req: Request, _res: Response, next: NextFunction): void { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { throw new AppError('未提供认证令牌', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.UNAUTHORIZED); } const token = authHeader.substring(7); try { const decoded = jwt.verify(token, config.jwt.secret) as JwtPayload; req.user = decoded; next(); } catch (error) { if (error instanceof jwt.TokenExpiredError) { throw new AppError('认证令牌已过期', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_EXPIRED); } throw new AppError('无效的认证令牌', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_INVALID); } } /** * 角色授权中间件 */ export function authorize(...roles: UserRole[]) { return (req: Request, _res: Response, next: NextFunction): void => { if (!req.user) { throw new AppError('未认证', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.UNAUTHORIZED); } if (!roles.includes(req.user.role)) { throw new AppError('没有权限执行此操作', HTTP_STATUS.FORBIDDEN, ERROR_CODES.FORBIDDEN); } next(); }; } /** * 生成访问令牌 */ export function generateAccessToken(payload: JwtPayload): string { return jwt.sign(payload, config.jwt.secret, { expiresIn: config.jwt.accessExpiresIn, }); } /** * 生成刷新令牌 */ export function generateRefreshToken(payload: JwtPayload): string { return jwt.sign(payload, config.jwt.secret, { expiresIn: config.jwt.refreshExpiresIn, }); } /** * 验证刷新令牌 */ export function verifyRefreshToken(token: string): JwtPayload { try { return jwt.verify(token, config.jwt.secret) as JwtPayload; } catch (error) { if (error instanceof jwt.TokenExpiredError) { throw new AppError('刷新令牌已过期', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_EXPIRED); } throw new AppError('无效的刷新令牌', HTTP_STATUS.UNAUTHORIZED, ERROR_CODES.TOKEN_INVALID); } }