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

NestJS Monorepo中如何创建通用外部JWT认证守卫?

NestJS Monorepo通用JWT认证守卫依赖注入解决方案

我的Monorepo结构如下,包含多个独立部署的NestJS应用,以及一个存放通用认证守卫的公共包:

- root
- - packages
- - - app1
- - - app2
- - - guards
- - - - src
- - - - dist

公司要求公共包构建后从dist目录导入,当前在应用中使用守卫的方式是:

// app1/src/main.ts
import { AuthGuard } from 'guards/dist';

async function bootstrap() {
  // ...
  app.useGlobalGuards(new AuthGuard());
  // ...
}

按照NestJS官方文档编写的守卫代码如下:

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // JWT解析逻辑...
    return true;
  }
}

现在的问题是:使用new AuthGuard()必须手动传入jwtService,但我不想每个应用都单独引入JwtService,违背了通用守卫的设计初衷。

我之前考虑的几个方案都有明显缺陷:

  • 将guards包改为导出模块:不清楚如何以此实现守卫
  • 弃用JwtService改用其他库:只能解决当前问题,无法应对未来类似场景
  • 手动实例化JwtService:违背Nest架构设计

正确解决方案:封装守卫模块,利用Nest的依赖注入机制

1. 在公共guards包中创建模块

在guards/src下创建auth-guard.module.ts,将守卫和JWT相关服务封装到模块中,同时支持传入JWT配置(让各个应用自定义密钥等参数):

import { Module, DynamicModule } from '@nestjs/common';
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
import { AuthGuard } from './auth.guard';

@Module({})
export class AuthGuardModule {
  static forRoot(options: JwtModuleOptions): DynamicModule {
    return {
      module: AuthGuardModule,
      imports: [JwtModule.register(options)],
      providers: [AuthGuard],
      exports: [AuthGuard], // 导出守卫供外部使用
    };
  }
}

2. 调整守卫代码,保持依赖注入

守卫代码无需修改,继续通过依赖注入获取JwtService:

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException();
    }
    try {
      const payload = await this.jwtService.verifyAsync(token);
      request.user = payload;
    } catch {
      throw new UnauthorizedException();
    }
    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

3. 在应用中导入模块并获取守卫

在应用的根模块(比如app1/src/app.module.ts)中导入公共守卫模块,传入JWT配置:

import { Module } from '@nestjs/common';
import { AuthGuardModule } from 'guards/dist';

@Module({
  imports: [
    AuthGuardModule.forRoot({
      secret: process.env.JWT_SECRET, // 每个应用可自定义密钥
      signOptions: { expiresIn: '60s' },
    }),
  ],
})
export class AppModule {}

然后在main.ts中,通过app.get()方法从Nest的依赖注入容器中获取守卫实例,无需手动new:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AuthGuard } from 'guards/dist';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 从容器中获取守卫实例,自动注入JwtService
  const authGuard = app.get(AuthGuard);
  app.useGlobalGuards(authGuard);
  await app.listen(3000);
}
bootstrap();

方案优势

  • 完全遵循Nest的依赖注入架构,不破坏设计原则
  • 通用守卫的逻辑集中维护,各个应用只需传入配置即可复用
  • 未来扩展其他依赖注入的公共组件时,可复用相同的模块封装方式

内容的提问来源于stack exchange,提问作者Ronny Efronny

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:17:39