import { Injectable, CanActivate, ExecutionContext, createParamDecorator, SetMetadata, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; import { UnauthorizedException } from '@/shared/exceptions/domain.exception'; export interface JwtPayload { userId: string; accountSequence: string; // 格式: D + YYMMDD + 5位序号 deviceId: string; type: 'access' | 'refresh'; } export interface CurrentUserData { userId: string; accountSequence: string; // 格式: D + YYMMDD + 5位序号 deviceId: string; } export const IS_PUBLIC_KEY = 'isPublic'; export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); export const CurrentUser = createParamDecorator( ( data: keyof CurrentUserData | undefined, ctx: ExecutionContext, ): CurrentUserData | string | number => { const request = ctx.switchToHttp().getRequest(); const user = request.user as CurrentUserData; return data ? user?.[data] : user; }, ); @Injectable() export class JwtAuthGuard implements CanActivate { constructor( private readonly jwtService: JwtService, private readonly reflector: Reflector, ) {} async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) return true; const request = context.switchToHttp().getRequest(); const token = this.extractTokenFromHeader(request); if (!token) throw new UnauthorizedException('缺少认证令牌'); try { const payload = await this.jwtService.verifyAsync(token); if (payload.type !== 'access') throw new UnauthorizedException('无效的令牌类型'); request.user = { userId: payload.userId, accountSequence: payload.accountSequence, deviceId: payload.deviceId, }; } catch { throw new UnauthorizedException('令牌无效或已过期'); } return true; } private extractTokenFromHeader(request: any): string | undefined { const [type, token] = request.headers.authorization?.split(' ') ?? []; return type === 'Bearer' ? token : undefined; } }