/** * JWT 认证守卫 * * 功能描述: * - 验证请求中的 JWT 令牌 * - 提取用户信息并添加到请求上下文 * - 保护需要认证的路由 * * @author kiro-ai * @version 1.0.0 * @since 2025-01-05 */ import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, Logger, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { Request } from 'express'; /** * JWT 载荷接口 */ export interface JwtPayload { sub: string; // 用户ID username: string; role: number; iat: number; // 签发时间 exp: number; // 过期时间 } /** * 扩展的请求接口,包含用户信息 */ export interface AuthenticatedRequest extends Request { user: JwtPayload; } @Injectable() export class JwtAuthGuard implements CanActivate { private readonly logger = new Logger(JwtAuthGuard.name); constructor(private readonly jwtService: JwtService) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const token = this.extractTokenFromHeader(request); if (!token) { this.logger.warn('访问被拒绝:缺少认证令牌'); throw new UnauthorizedException('缺少认证令牌'); } try { // 验证并解码 JWT 令牌 const payload = await this.jwtService.verifyAsync(token); // 将用户信息添加到请求对象 (request as AuthenticatedRequest).user = payload; this.logger.log(`用户认证成功: ${payload.username} (ID: ${payload.sub})`); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : '未知错误'; this.logger.warn(`JWT 令牌验证失败: ${errorMessage}`); throw new UnauthorizedException('无效的认证令牌'); } } /** * 从请求头中提取 JWT 令牌 * * @param request 请求对象 * @returns JWT 令牌或 undefined */ private extractTokenFromHeader(request: Request): string | undefined { const [type, token] = request.headers.authorization?.split(' ') ?? []; return type === 'Bearer' ? token : undefined; } }