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

NestJS+GraphQL如何从登录返回的用户ID自动注入Resolver?

在NestJS GraphQL中自动注入当前用户ID的实现方案

不用手动传userId的核心思路是:通过JWT守卫解析登录返回的token,把用户信息挂载到请求上下文,再用自定义装饰器在Resolver中直接获取。以下是具体步骤:

1. 实现JWT认证守卫

首先需要一个守卫来验证请求头里的JWT token,解析出用户ID并挂载到GraphQL的上下文(Context)中。如果还没配置JWT模块,先确保依赖已安装:

npm install @nestjs/jwt passport-jwt passport
npm install -D @types/passport-jwt

编写JWT守卫

import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { GqlExecutionContext } from '@nestjs/graphql';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  getRequest(context: ExecutionContext) {
    // 转换GraphQL上下文为HTTP请求对象
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }
}

配置JWT策略

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get('JWT_SECRET'), // 替换成你的JWT密钥
    });
  }

  async validate(payload: any) {
    // 这里的payload是JWT的内容,登录时要把userId存入token
    return { userId: payload.sub }; // 返回的对象会挂载到request.user上
  }
}

注册JWT模块

在AuthModule中完成配置注册:

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    PassportModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        secret: configService.get('JWT_SECRET'),
        signOptions: { expiresIn: '60m' },
      }),
      inject: [ConfigService],
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [JwtModule],
})
export class AuthModule {}

2. 创建自定义装饰器获取用户ID

写一个装饰器,从上下文的request.user中提取userId:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

export const CurrentUserId = createParamDecorator(
  (data: unknown, context: ExecutionContext) => {
    const ctx = GqlExecutionContext.create(context);
    const request = ctx.getContext().req;
    return request.user.userId; // 对应JwtStrategy中返回的userId字段
  },
);

3. 在Resolver中使用装饰器

现在可以直接在Resolver的方法参数里用@CurrentUserId()获取userId,不用手动传参了:

import { Query, Mutation, Resolver, UseGuards } from '@nestjs/graphql';
import { EventsService } from './events.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CurrentUserId } from '../auth/current-user-id.decorator';

@Resolver()
@UseGuards(JwtAuthGuard) // 全局守卫,也可以单独给某个方法加
export class EventsResolver {
  constructor(private readonly eventsService: EventsService) {}

  @Query(() => [Event])
  getMyEvents(@CurrentUserId() userId: string) {
    return this.eventsService.getEventsByUserId(userId);
  }

  @Mutation(() => Event)
  createEvent(
    @CurrentUserId() userId: string,
    @Args('input') input: CreateEventInput,
  ) {
    return this.eventsService.createEvent(userId, input);
  }
}

关键说明

  • 为什么Interceptor不行?Interceptor的执行时机在Guard之后,但它的核心职责是请求/响应的转换,而认证逻辑(解析token、验证用户合法性)属于Guard的职责范围。必须先通过Guard完成认证并挂载用户信息,Resolver才能安全获取。
  • 登录时要确保AuthService.login生成token时,把userId存入payload的sub字段(或自定义字段),对应JwtStrategy里的payload.sub。示例:
async login(user: User) {
  const payload = { sub: user.id, username: user.username };
  return {
    access_token: this.jwtService.sign(payload),
    user,
  };
}

内容的提问来源于stack exchange,提问作者Jesús Iasenzaniro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 13:07:40