6月2日 01:23

NestJS 怎么做认证和授权?JWT、Guards 和 RBAC 实战

NestJS 的认证授权用 Guards(守卫)+ 策略模式实现。认证(Authentication)验证"你是谁",授权(Authorization)验证"你能做什么"。JWT 是最常用的认证方案,RBAC 是最常用的授权模型。

JWT 认证

安装依赖:

bash
npm install @nestjs/jwt @nestjs/passport passport passport-jwt

配置 JWT 策略

typescript
// auth/jwt.strategy.ts @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: process.env.JWT_SECRET, }); } async validate(payload: { sub: string; email: string }) { return { id: payload.sub, email: payload.email }; } }

validate 的返回值会挂到 req.user 上。Passport 自动验证 JWT 签名和过期时间,你只需要解析 payload。

登录签发 Token

typescript
// auth/auth.service.ts @Injectable() export class AuthService { constructor(private jwtService: JwtService) {} async login(email: string, password: string) { const user = await this.validateUser(email, password); const payload = { sub: user.id, email: user.email }; return { access_token: this.jwtService.sign(payload), }; } }

保护路由

typescript
@UseGuards(AuthGuard('jwt')) @Get('profile') getProfile(@Request() req) { return req.user; }

没带 Token 或 Token 过期的请求返回 401。

RBAC 角色授权

认证只解决"你是谁",授权解决"你能做什么"。

typescript
// auth/roles.decorator.ts export const Roles = (...roles: string[]) => SetMetadata('roles', roles); // auth/roles.guard.ts @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler()); if (!requiredRoles) return true; const { user } = context.switchToHttp().getRequest(); return requiredRoles.some(role => user.roles?.includes(role)); } }

使用:

typescript
@UseGuards(AuthGuard('jwt'), RolesGuard) @Roles('admin') @Delete('users/:id') removeUser() { return '仅管理员可操作'; }

先过 AuthGuard 验证身份(拿到 user),再过 RolesGuard 验证角色。顺序不能反——没有 user 对象就没法检查角色。

密码安全

永远不要明文存密码。用 bcrypt 哈希:

typescript
import * as bcrypt from 'bcrypt'; // 注册时哈希 const hash = await bcrypt.hash(password, 10); // 登录时验证 const isValid = await bcrypt.compare(inputPassword, hash);

10 是 salt rounds,越大越安全但越慢。10 是当前推荐值,每增加 1 耗时翻倍。

常见安全措施

1. 限流防暴力破解:用 @nestjs/throttler 限制登录接口的请求频率。

typescript
ThrottlerModule.forRoot([{ ttl: 60000, limit: 5 }]),

60 秒内同一 IP 最多 5 次登录请求。

2. CORS 配置:只允许可信域名访问。

typescript
app.enableCors({ origin: ['https://your-app.com'] });

3. Helmet 设置安全头npm i helmetapp.use(helmet())。自动加上 X-Content-Type-Options、X-Frame-Options 等安全响应头。

4. 输入验证:用 class-validator 的 DTO 验证所有入参,防止注入攻击。

typescript
export class CreateUserDto { @IsEmail() email: string; @MinLength(8) password: string; }

Token 刷新

JWT 一旦签发无法撤销(这是无状态的设计)。如果用户改了密码或被踢下线,旧的 Token 仍然有效直到过期。

两种解决方式:

  • 短过期 + Refresh Token:access_token 15 分钟过期,refresh_token 7 天过期。refresh_token 存数据库可以主动撤销
  • Token 黑名单:过期前把 Token 加入 Redis 黑名单,每次请求检查黑名单。牺牲了无状态的优点但更安全
标签:NestJS