NestJS 守卫是一个实现了 CanActivate 接口的类。
一、它是什么?
在 NestJS 里,「守卫(Guard)」是一种用来控制请求是否能进入路由处理器(Controller 方法) 的机制。
通俗点说:
守卫就是"门卫"------每次请求进来之前,它会先检查一下你有没有资格进去。
- 通过进入下一步
- 未通过❌,请求拒绝(比如返回 403 Forbidden)
核心职责 :主要关注 授权(Authorization) 。虽然也可以做认证(Authentication),但通常认证由中间件或 Passport 策略处理,而守卫用于更细粒度的权限控制(如:只有管理员才能删除文章)。
在框架生命周期中,守卫的执行时机是:
请求进入 → 中间件 → 守卫 → 拦截器 → 管道 → 控制器 → 服务
二、怎么用?
创建守卫
kotlin
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
// 简单示例:如果请求头中有 token,就放行
const token = request.headers['authorization'];
if (token) {
return true; // 放行
}
// 否则拒绝
return false;
}
}
canActivate()方法返回:
true→ 允许进入控制器;false→ 阻止访问(会返回 403 Forbidden);- 也可以返回一个
Promise<boolean>或Observable<boolean>(支持异步)。
应用守卫
你可以在三个层级使用守卫:
1. 方法级
kotlin
import { UseGuards, Controller, Get } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('user')
export class UserController {
@Get('profile')
@UseGuards(AuthGuard)
getProfile() {
return { msg: '用户资料' };
}
}
2. 控制器级
less
@UseGuards(AuthGuard)
@Controller('admin')
export class AdminController {
@Get()
getAdminData() {
return '后台数据';
}
}
3. 全局守卫
javascript
// main.ts
import { AppModule } from './app.module';
import { AuthGuard } from './auth.guard';
import { NestFactory } from '@nestjs/core';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
await app.listen(3000);
}
bootstrap();
三、使用场景
守卫最常见的用途是:权限控制 / 身份验证。
- 身份验证(Authentication) :检查用户是否已登录(例如验证 JWT Token 是否存在且有效)。
- 角色授权(Role-based Authorization) :检查当前用户是否拥有特定角色(如
admin,editor)。 - 权限控制(Permission-based Authorization) :检查用户是否有执行特定操作的权限(如:
user:delete)。 - IP 地址过滤:只允许特定 IP 段的请求访问。
- 功能开关:根据配置动态开启或关闭某些接口。
- 请求时间限制(比如只允许工作时间访问)
你可以把它理解为:
在"进入接口之前"的最后一道防线。
四、中间件 vs 守卫
1. 中间件
中间件 是通用的"流水线工人",负责处理请求的通用逻辑(如日志、解析数据),它不知道具体的业务逻辑是什么。
中间件的盲区 : 当中间件运行时,NestJS 还没有确定最终由哪个 Controller 的哪个方法来处理请求。因此,中间件无法知道当前请求是否需要"管理员权限"。你无法在中间件里写:"如果这个路由用了
@Roles('admin')装饰器,则检查角色"。
2. 守卫:
守卫 是专业的"安检员",专门负责授权决策 (能不能进),它完全知道即将执行哪个具体的控制器方法,并能根据元数据做判断。
守卫的全知视角:
守卫接收
ExecutionContext对象。通过这个对象,你可以拿到:
context.getClass(): 当前的 Controller 类。context.getHandler(): 当前正在执行的方法。- 结合
Reflector,你可以读取该方法上所有的装饰器元数据(例如@Roles('admin'))。- 结论 :凡是需要根据路由元数据 (装饰器)来做判断的逻辑,必须用守卫。
3. 核心区别对比表
| 特性 | 中间件 (Middleware) | 守卫 (Guard) |
|---|---|---|
| 主要职责 | 通用逻辑:日志、压缩、Cookie 解析、原始请求预处理。 | 授权 (Authorization) :决定请求是否允许执行特定的 Handler。 |
| 执行时机 | 最早。在守卫、拦截器、管道之前执行。 | 中间。在中间件之后,拦截器和管道之前执行。 |
| 上下文感知 | 弱 。只知道 req 和 res,不知道具体要调用哪个 Controller 或哪个方法。 |
强 。拥有 ExecutionContext,知道具体的 Class、Handler 方法、参数类型等。 |
| 访问元数据 | 无法直接访问 路由装饰器(如 @Roles, @Get)定义的元数据。 |
可以访问 。配合 Reflector 可以轻松读取路由上的自定义元数据。 |
| 返回值/控制流 | 必须调用 next() 才能继续,或者直接 res.end() 结束响应。 |
返回 boolean (或 Promise/Observable)。true 放行,false 拒绝(抛出异常)。 |
| 依赖注入 | 支持,但配置稍显繁琐(通常通过 forRoot 或模块配置)。 |
完美支持,像普通 Service 一样注入依赖。 |
| 适用场景 | 记录所有请求日志、解析 JSON/Cookie、设置 CORS、Gzip 压缩。 | 检查 JWT、验证用户角色、IP 白名单、基于权限的访问控制。 |