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
相关产品推荐
相关产品推荐

