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

NestJS 9.4.2中如何用自定义装饰器验证私有HTTP请求权限?

实现NestJS私有HTTP请求验证(基于自定义装饰器与.env变量)

步骤1:配置环境变量与依赖

首先安装@nestjs/config来读取.env文件:

npm install @nestjs/config

在项目根目录的.env中添加验证密钥:

PRIVATE_API_KEY=your-secure-secret-key-here

然后在根模块中全局配置ConfigModule:

// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 全局注入,无需在子模块重复导入
    }),
    AuthModule,
  ],
})
export class AppModule {}

步骤2:创建私有接口守卫

守卫负责核心验证逻辑,对比请求中的密钥与.env配置值:

// src/common/guards/private.guard.ts
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Reflector } from '@nestjs/core';

// 自定义元数据键,用于标记私有接口
const IS_PRIVATE = 'isPrivate';

@Injectable()
export class PrivateGuard implements CanActivate {
  constructor(
    private readonly configService: ConfigService,
    private readonly reflector: Reflector,
  ) {}

  canActivate(context: ExecutionContext): boolean {
    // 检查当前接口是否被标记为私有
    const isPrivate = this.reflector.getAllAndOverride<boolean>(IS_PRIVATE, [
      context.getHandler(),
      context.getClass(),
    ]);

    // 非私有接口直接放行
    if (!isPrivate) return true;

    // 从请求Header中获取客户端提供的密钥
    const request = context.switchToHttp().getRequest();
    const providedKey = request.headers['x-api-key'];
    const validKey = this.configService.get<string>('PRIVATE_API_KEY');

    // 验证密钥有效性
    if (!providedKey || providedKey !== validKey) {
      throw new UnauthorizedException('无效或缺失API密钥');
    }

    return true;
  }
}

步骤3:创建自定义装饰器标记私有接口

用装饰器快速标记需要验证的接口:

// src/common/decorators/private.decorator.ts
import { SetMetadata } from '@nestjs/common';

const IS_PRIVATE = 'isPrivate';
export const Private = () => SetMetadata(IS_PRIVATE, true);

步骤4:在控制器中应用验证逻辑

修改你的login接口,添加装饰器和守卫:

import { Controller, Post, Body, UseGuards } from '@nestjs/common';
import { Private } from '../common/decorators/private.decorator';
import { PrivateGuard } from '../common/guards/private.guard';
import { AuthDto } from './dto/auth.dto';
import { LoginService } from './login.service';
import { AuthService } from './auth.service';
import { BadRequestException } from '@nestjs/common';

@Controller('auth')
export class AuthController {
  constructor(
    private readonly loginService: LoginService,
    private readonly authService: AuthService,
  ) {}

  @Post('/login')
  @Private() // 标记该接口为私有
  @UseGuards(PrivateGuard) // 应用验证守卫
  async login(@Body() body: AuthDto) {
    try {
      const usuario: IUser[] = await this.loginService.getUser(body);
      const tokens = await this.authService.getTokens(usuario);
      return tokens;
    } catch (error) {
      throw new BadRequestException('请求失败', {
        cause: error,
        description: '用户查询或令牌生成出错',
      });
    }
  }
}

验证逻辑说明

  • 客户端请求时需在Header中携带X-API-Key字段,值与.env中的PRIVATE_API_KEY一致
  • 守卫会自动检查接口是否标记为私有,若未标记则直接放行;若标记则验证密钥有效性
  • 密钥不匹配或缺失时返回401 Unauthorized错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 14:05:33