You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 18:21:27