NestJS 系列教程(五):守卫(Guards)与 JWT 用户认证
🛡️ NestJS 系列教程(五):守卫(Guards)与 JWT 用户认证
✨ 本篇目标
你将学会:
- 什么是守卫(Guard)?与中间件有何不同?
- 如何使用守卫进行访问控制
- 使用 Passport 和 JWT 实现用户登录认证
- 基于角色控制访问权限
🤔 守卫(Guard)是什么?
守卫是用于控制请求是否被执行的机制,通常用于认证和授权。
它拦截请求,执行逻辑判断:
- 如果返回
true
,请求继续; - 如果返回
false
或抛出异常,请求被拒绝。
与中间件不同的是,守卫能访问路由处理上下文(比如 handler metadata)和参数装饰器的值,适合做「权限控制」。
🧱 搭建 Auth 模块
创建认证模块及其控制器和服务:
nest g module auth
nest g controller auth
nest g service auth
安装 Passport 和 JWT:
npm install @nestjs/passport passport @nestjs/jwt passport-jwt
npm install --save-dev @types/passport-jwt
🔐 AuthService(模拟用户登录)
在 auth.service.ts
中:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';@Injectable()
export class AuthService {constructor(private jwtService: JwtService) {}// 模拟验证用户名和密码async validateUser(username: string, pass: string): Promise<any> {const user = { userId: 1, username: 'admin', password: '123456', role: 'admin' };if (username === user.username && pass === user.password) {const { password, ...result } = user;return result;}return null;}async login(user: any) {const payload = { username: user.username, sub: user.userId, role: user.role };return {access_token: this.jwtService.sign(payload),};}
}
📦 配置 AuthModule 与 JWT
在 auth.module.ts
中:
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';@Module({imports: [JwtModule.register({secret: 'my-secret-key', // 实际项目应放在 .env 文件signOptions: { expiresIn: '1h' },}),],controllers: [AuthController],providers: [AuthService, JwtStrategy],exports: [AuthService],
})
export class AuthModule {}
✍️ 编写登录接口
在 auth.controller.ts
中:
import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';@Controller('auth')
export class AuthController {constructor(private readonly authService: AuthService) {}@Post('login')async login(@Body() body: any) {const user = await this.authService.validateUser(body.username, body.password);if (!user) {throw new UnauthorizedException('Invalid credentials');}return this.authService.login(user);}
}
🧠 实现 JWT 策略(Strategy)
创建 jwt.strategy.ts
:
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {constructor() {super({jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),ignoreExpiration: false,secretOrKey: 'my-secret-key',});}async validate(payload: any) {return { userId: payload.sub, username: payload.username, role: payload.role };}
}
🛡️ 创建 JWT 守卫(Guard)
创建 jwt-auth.guard.ts
:
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
使用方式:
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {return req.user;
}
🧑⚖️ 创建角色守卫(RoleGuard)
定义 roles.decorator.ts
:
import { SetMetadata } from '@nestjs/common';export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
定义 roles.guard.ts
:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';@Injectable()
export class RolesGuard implements CanActivate {constructor(private reflector: Reflector) {}canActivate(context: ExecutionContext): boolean {const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [context.getHandler(),context.getClass(),]);if (!requiredRoles) return true;const { user } = context.switchToHttp().getRequest();return requiredRoles.includes(user.role);}
}
使用方式:
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Get('admin')
getAdminStuff() {return '只有管理员能看到';
}
🧪 测试认证流程
- 调用
POST /auth/login
获取 token - 在后续请求中添加 Header:
Authorization: Bearer <token>
- 访问受保护路由
/cats/admin
,根据角色控制访问权限
✅ 本章小结
你已经学会:
- 守卫的原理和使用场景
- 使用 JWT + Passport 实现用户登录认证
- 编写角色守卫控制访问权限
- 使用装饰器、策略和守卫构建灵活的权限体系
🔮 下一篇预告
第6篇:拦截器(Interceptor)与统一响应格式封装
我们将讲解如何使用拦截器对响应结果统一封装、记录日志、实现响应缓存等能力。