给 NestJS 增加一个全局 Guard

在 NestJS 框架中,Guard 是一种强大的中间件机制,用于在执行控制器方法之前对 HTTP 请求进行预处理与验证。本篇博客将详细介绍一个名为 GlobalGuard 的全局 Guard 实现,该 Guard 负责检查请求的认证信息,并根据需要调用 AuthService 里的方法进行令牌验证。以下是对关键代码段的详细解读。

一、依赖注入与类定义

@Injectable()
export class GlobalGuard implements CanActivate {
  constructor(
    private readonly authService: AuthService,
    private readonly reflector: Reflector
  ) {}

首先,GlobalGuard 被声明为一个 @Injectable() 装饰器修饰的类,表明它是一个可供依赖注入系统管理的服务。同时,GlobalGuard 实现了 NestJS 提供的 CanActivate 接口,这意味着它具备了拦截 HTTP 请求并在其到达控制器方法前进行预处理的能力。

在构造函数中,我们注入了两个依赖:

  • AuthService: 用于处理与用户认证相关的逻辑,如令牌验证。这里假设 AuthService 提供了一个 validateToken 方法,用于验证并返回用户信息。(这里的方法需要自己写。)

  • Reflector: NestJS 提供的反射工具,用于获取控制器方法、路由等上的元数据,便于动态调整 Guard 的行为。

二、实现 canActivate 方法

async canActivate(context: ExecutionContext): Promise<boolean> {
  // ... 具体实现 ...
}

canActivateCanActivate 接口要求实现的方法,接收一个ExecutionContext 参数,表示当前正在处理的 HTTP请 求上下文。此方法必须返回一个 Promise<boolean>,表示是否允许请求继续执行对应的控制器方法。接下来,我们将详细解析 canActivate 的内部逻辑。

1. 检查请求是否公开

const isPublic = this.reflector.get<boolean>(
  "isPublic",
  context.getHandler()
);
if (isPublic) {
  return true;
}

使用 reflector 从当前请求对应的控制器方法上获取名为 isPublic 的元数据。如果该元数据存在且为 true,说明该请求无需认证即可访问,Guard 直接允许请求通过。

2. 检查认证头

const authorizationHeader = request.headers.authorization;

if (!authorizationHeader) {
  throw new HttpException("缺少认证头。", HttpStatus.UNAUTHORIZED);
}

const [type, token] = authorizationHeader.split(" ");
if (type !== "Bearer" || !token) {
  throw new HttpException("认证头格式错误。", HttpStatus.UNAUTHORIZED);
}

首先从请求头中提取 authorization 字段。若该字段不存在,Guard 抛出一个 HttpException,状态码为 UNAUTHORIZED(401),提示“缺少认证头”。

接着,对 authorization 字段进行拆分,期望得到形如 Bearer <token> 的格式。若拆分后的类型不是 Bearer 或没有有效的令牌,Guard 再次抛出 HttpException,状态码为 UNAUTHORIZED,提示“认证头格式错误”。

3. 验证令牌并设置用户数据

try {
  request.userData = await this.authService.validateToken(token);
  return true;
} catch (error) {
  throw new HttpException("无效或过期的令牌。", HttpStatus.UNAUTHORIZED);
}

调用 AuthServicevalidateToken 方法,传入提取的令牌进行验证。如果验证成功,将返回的用户信息存储在 request.userData 中,以便后续控制器方法使用。Guard 最后返回 true,允许请求继续执行。

validateToken 方法抛出异常(通常表示令牌无效或已过期),Guard 捕获该异常并抛出一个新的 HttpException,状态码为UNAUTHORIZED,提示“无效或过期的令牌”。

结语

至此,我们详细剖析了 NestJS 项目中 GlobalGuard 的实现细节。该 Guard 作为全局认证防护层,有效地检查了 HTTP 请求的认证头,利用 AuthService 的方法来验证令牌,并根据验证结果决定是否允许请求继续执行。同时,通过 Reflector 支持灵活地为特定请求方法标记为无需认证,体现了 NestJS Guard 机制的强大与灵活性。在实际项目中,这样的全局 Guard 能够极大地简化认证相关的代码组织与维护工作。

完整代码

import {
  Injectable,
  CanActivate,
  ExecutionContext,
  HttpException,
  HttpStatus,
} from "@nestjs/common";
import { Reflector } from "@nestjs/core";

import { AuthService } from "src/auth/auth.service";

@Injectable()
export class GlobalGuard implements CanActivate {
  constructor(
    private readonly authService: AuthService,
    private readonly reflector: Reflector
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const authorizationHeader = request.headers.authorization;

    // 检查当前请求是否标记为公开(无需认证),公开则直接通过。
    const isPublic = this.reflector.get<boolean>(
      "isPublic",
      context.getHandler()
    );
    if (isPublic) {
      return true;
    }

    if (!authorizationHeader) {
      throw new HttpException("缺少认证头。", HttpStatus.UNAUTHORIZED);
    }

    const [type, token] = authorizationHeader.split(" ");

    if (type !== "Bearer" || !token) {
      throw new HttpException("认证头格式错误。", HttpStatus.UNAUTHORIZED);
    }

    try {
      request.userData = await this.authService.validateToken(token); // 假设 validateToken 方法用于验证并返回用户信息
      return true;
    } catch (error) {
      throw new HttpException("无效或过期的令牌。", HttpStatus.UNAUTHORIZED);
    }
  }
}
  • 33
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

narukeu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值