创建日期:2026-09-08 | 最近更新:2026-09-08 基于 NestJS 12 本机实测:下方 401/200 返回均为真实 curl 输出(自定义
AuthGuard);JWT/Passport 写法以@nestjs/jwt、@nestjs/passport官方文档为准。
鉴权:Guard / JWT / RBAC
一句话:Nest 里「能不能访问这个接口」由 Guard 决定——它在你设计的中间件之后、controller 之前执行,返回
true放行、抛异常拒绝。Guard 能读元数据,所以它能实现「这个路由要admin角色」这种声明式权限控制,而不是在每个方法里手写 if。
1. 先搞清请求处理顺序
请求 → 中间件(middleware) → 守卫(guard) → 拦截器(interceptor,before) → 管道(pipe) → 处理器(handler)
↑
真正到 controller 之前,鉴权已经过了
Guard 负责「放不放进来」;验证通过后,怎么把用户信息传给 handler,通常靠 @Req() 或自定义装饰器。
2. 手写第一个 Guard(本机实测)
// common/auth.guard.ts
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import type { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest<Request>();
if (req.headers['x-token'] === 'nest-secret') return true; // 示意:真项目验 JWT
throw new UnauthorizedException('缺少或错误的 x-token');
}
}
用法:路由/控制器上挂 @UseGuards(AuthGuard)。
@Get('protected')
@UseGuards(AuthGuard)
protectedData() { return { secret: 'top-secret-data' }; }
实测输出(Nest 12,真实 curl):
⑤ GET /protected 不带 token
→ 401 {"message":"缺少或错误的 x-token","error":"Unauthorized","statusCode":401}
⑥ GET /protected 带 x-token: nest-secret
→ 200 {"secret":"top-secret-data"}
注意 ⑤:根本没进 controller——canActivate 抛的 UnauthorizedException 直接由全局异常过滤器转成了 401。鉴权失败就是「进不了门」,而不是在业务代码里补救。
3. 全局 Guard 与依赖注入
想「全应用默认都要登录」:
// app.module.ts
providers: [
{ provide: APP_GUARD, useClass: AuthGuard }, // 全局守卫
],
个别接口要公开,在路由上加 @Public()(自己用 SetMetadata 定义)+ 守卫里放行白名单。Guard 是普通的 provider,能注入别的 provider(比如 JwtService、UsersService),所以「读 DB 验证用户」也自然。
4. 元数据驱动的 RBAC:Roles 装饰器 + Reflector
「某些接口只有 admin 能调」的正确姿势是用元数据声明,别在 handler 里写死角色判断:
// roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
// roles.guard.ts —— 用 Reflector 读「这个路由声明了哪些角色」
import { CanActivate, ExecutionContext, Injectable, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from './roles.decorator.js';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(), // 方法级元数据
context.getClass(), // 类级元数据(兜底)
]);
if (!roles) return true; // 没标 Roles → 不设限
const { user } = context.switchToHttp().getRequest();
if (roles.some((r) => user?.roles?.includes(r))) return true;
throw new ForbiddenException('权限不足');
}
}
@Post('admin')
@Roles('admin') // 声明式:只有 admin 能调
adminThing() { … }
这套「@Roles 声明 + Guard 读取 + 处理器里 @Req().user」就是 Nest 生态里 RBAC 的标准形态。配合 Passport/JWT,user 通常是「鉴权守卫验完 token 后塞进 request」的登录用户。
5. JWT 实战(认证 + 鉴权两步)
登录:签发 token
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private readonly jwt: JwtService) {}
login(user: { id: number; roles: string[] }) {
return { accessToken: await this.jwt.signAsync(
{ sub: user.id, roles: user.roles },
{ secret: process.env.JWT_SECRET, expiresIn: '2h' },
)};
}
}
鉴权守卫:验 token → 塞 user
// jwt-auth.guard.ts(把上面 AuthGuard 里硬编码校验换成真验证)
const [type, token] = (req.headers.authorization ?? '').split(' ');
if (type !== 'Bearer' || !token) throw new UnauthorizedException();
try {
req.user = await this.jwt.verifyAsync(token, { secret: process.env.JWT_SECRET });
return true;
} catch {
throw new UnauthorizedException('token 无效或过期');
}
把两段合起来的推荐顺序:
JwtAuthGuard(验 token,塞 req.user)
↓
RolesGuard(读 @Roles 元数据 + req.user.roles 判权限)
两者都用 APP_GUARD 或叠加 @UseGuards(JwtAuthGuard, RolesGuard) 都行。
也可用
@nestjs/passport+passport-jwt的 Strategy 写法(官方示例居多);本质一样:Strategy 负责「验什么、user 长什么样」,Guard 触发它。两种选一种即可,别混。
别踩的坑
- secret 别写死进代码:放
process.env.JWT_SECRET(配合@nestjs/config),生产轮换要平滑; - token 一定要有过期时间:
expiresIn,配合刷新 token 方案; - 存哪:Web 端 不要塞 localStorage 裸用,至少 HttpOnly cookie / 内存 + 刷新策略(前端安全另论);
- 角色别只信前端传来,角色从服务端查/由 token 声明,Guard 里以服务端为准。
6. 小结
| 想实现 | 用什么 |
|---|---|
| 某些接口需要登录 | 自定义 AuthGuard + @UseGuards 或 APP_GUARD 全局 |
| 声明式角色控制(RBAC) | @Roles('admin') 元数据 + RolesGuard(Reflector) |
| 签发/验证 JWT | @nestjs/jwt 的 signAsync/verifyAsync(或 passport-jwt) |
| 有些路由公开 | @Public() 元数据 + 守卫放行白名单 |
| 登录用户怎么传给 handler | Guard 里 req.user = …,再用 @Req()/自定义装饰器取 |
关联
- 上一篇:DTO / ValidationPipe
- 下一篇:测试与部署
- 参考:Nest 官方 Guards、Authentication