NestJS中如何在中间件中获取JwtAuthGuard解析的request.user?
解决NestJS中中间件无法获取Guard设置的req.user问题
你遇到的核心问题是NestJS的执行顺序:中间件先于Guard执行,所以SpaceMemberMiddleware无法拿到JwtAuthGuard设置的req.user。以下是三种可行的解决方案:
方案一:扩展JwtAuthGuard,在认证后获取Member
既然Guard在中间件之后执行,且能访问request对象,可以直接在JwtAuthGuard的canActivate方法中完成Member的获取和附加,无需单独的中间件。
修改后的JwtAuthGuard代码:
@Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { constructor( private reflector: Reflector, private readonly membersService: MembersService ) { super(); } async canActivate(context: ExecutionContext): Promise<boolean> { const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) return true; // 先完成JWT认证 const authResult = await super.canActivate(context); if (!authResult) return false; // 获取当前请求对象 const request = context.switchToHttp().getRequest(); const user: User = request.user; // 查询Member并附加到请求 const member = await this.membersService.findByUserId(user.userId); if (!member) throw new BadRequestException(); request.member = member; return true; } }
优点:将认证与Member获取逻辑整合,减少冗余;无需调整中间件顺序。
方案二:使用Interceptor实现Member获取
Interceptor的执行顺序在Guard之后,可以安全访问req.user,适合需要将Member获取逻辑与认证解耦的场景。
创建SpaceMemberInterceptor:
@Injectable() export class SpaceMemberInterceptor implements NestInterceptor { constructor(private readonly membersService: MembersService) {} async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> { const request = context.switchToHttp().getRequest(); const user: User = request.user; const member = await this.membersService.findByUserId(user.userId); if (!member) throw new BadRequestException(); request.member = member; return next.handle(); } }
在控制器中组合使用Guard和Interceptor:
@UseGuards(JwtAuthGuard) @UseInterceptors(SpaceMemberInterceptor) @Controller('spaces') export class SpacesController { // 控制器方法 }
优点:逻辑分离清晰,可单独复用Interceptor;对原有Guard无侵入。
方案三:将JWT认证改为中间件
如果必须使用中间件形式的Member获取,可以将JWT认证逻辑改为中间件,确保认证中间件先于SpaceMemberMiddleware执行。
创建JWT认证中间件:
@Injectable() export class JwtAuthMiddleware implements NestMiddleware { constructor( private readonly passport: Passport, private readonly usersService: UsersService ) {} use(req: any, res: any, next: () => void) { this.passport.authenticate('jwt', { session: false }, async (err, payload) => { if (err || !payload) { throw new UnauthorizedException(); } // 从payload查询用户,对应原Strategy的validate逻辑 const user = await this.usersService.findOneByCognitoId(payload.username); if (!user) throw new UnauthorizedException(); req.user = user; next(); })(req, res, next); } }
在模块中配置中间件顺序:
export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(JwtAuthMiddleware, SpaceMemberMiddleware) .forRoutes('spaces'); // 指定需要应用的路由 } }
注意:需要自行处理公共路由的跳过逻辑(比如在JwtAuthMiddleware中判断是否有@Public()装饰器)。
优点:严格遵循中间件执行顺序,符合你最初的中间件思路。
内容的提问来源于stack exchange,提问作者Yael Badillo
相关产品推荐
相关产品推荐

