NestJS守卫(Guard) #
什么是守卫? #
守卫是用@Injectable()装饰器装饰的类,它实现了CanActivate接口。守卫决定请求是否应该被路由处理程序处理,主要用于权限控制和认证。
守卫与中间件的区别 #
| 特性 | 中间件 | 守卫 |
|---|---|---|
| 执行时机 | 路由匹配之前 | 路由匹配之后 |
| 访问ExecutionContext | 否 | 是 |
| 主要用途 | 日志、请求处理 | 权限控制、认证 |
| 抛出异常 | 需要手动处理 | 自动处理 |
创建守卫 #
基本守卫 #
typescript
import {
Injectable,
CanActivate,
ExecutionContext,
} from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return this.validateRequest(request);
}
private validateRequest(request: any): boolean {
return !!request.headers.authorization;
}
}
ExecutionContext #
ExecutionContext提供了请求的上下文信息:
typescript
interface ExecutionContext extends ArgumentsHost {
getClass<T>(): Type<T>; // 获取控制器类
getHandler(): Function; // 获取处理方法
}
应用守卫 #
方法级别 #
typescript
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('users')
export class UsersController {
@Get()
@UseGuards(AuthGuard)
findAll() {
return 'Protected route';
}
}
控制器级别 #
typescript
@Controller('users')
@UseGuards(AuthGuard)
export class UsersController {
@Get()
findAll() {}
@Get('profile')
getProfile() {}
}
全局级别 #
typescript
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
认证守卫 #
JWT认证守卫 #
typescript
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException('Token not found');
}
try {
const payload = await this.jwtService.verifyAsync(token);
request.user = payload;
} catch {
throw new UnauthorizedException('Invalid token');
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
使用认证守卫 #
typescript
@Controller('users')
@UseGuards(JwtAuthGuard)
export class UsersController {
@Get('profile')
getProfile(@Request() req) {
return req.user;
}
}
角色守卫 #
定义角色 #
typescript
export enum Role {
USER = 'user',
ADMIN = 'admin',
SUPER_ADMIN = 'super_admin',
}
角色装饰器 #
typescript
import { SetMetadata } from '@nestjs/common';
import { Role } from './role.enum';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
角色守卫实现 #
typescript
import {
Injectable,
CanActivate,
ExecutionContext,
ForbiddenException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Role } from './role.enum';
import { ROLES_KEY } from './roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
if (!user) {
throw new ForbiddenException('User not authenticated');
}
const hasRole = requiredRoles.some(role => user.role === role);
if (!hasRole) {
throw new ForbiddenException('Insufficient permissions');
}
return true;
}
}
使用角色守卫 #
typescript
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(Role.ADMIN)
export class AdminController {
@Get('users')
findAllUsers() {
return 'All users';
}
@Get('settings')
@Roles(Role.SUPER_ADMIN)
getSettings() {
return 'Admin settings';
}
}
权限守卫 #
权限装饰器 #
typescript
import { SetMetadata } from '@nestjs/common';
export const PERMISSIONS_KEY = 'permissions';
export const RequirePermissions = (...permissions: string[]) =>
SetMetadata(PERMISSIONS_KEY, permissions);
权限守卫 #
typescript
import {
Injectable,
CanActivate,
ExecutionContext,
ForbiddenException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
PERMISSIONS_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredPermissions) {
return true;
}
const { user } = context.switchToHttp().getRequest();
if (!user || !user.permissions) {
throw new ForbiddenException('No permissions');
}
const hasAllPermissions = requiredPermissions.every(permission =>
user.permissions.includes(permission),
);
if (!hasAllPermissions) {
throw new ForbiddenException('Insufficient permissions');
}
return true;
}
}
使用权限守卫 #
typescript
@Controller('users')
@UseGuards(JwtAuthGuard, PermissionsGuard)
export class UsersController {
@Get()
@RequirePermissions('user:read')
findAll() {
return 'All users';
}
@Post()
@RequirePermissions('user:create')
create() {
return 'User created';
}
@Delete(':id')
@RequirePermissions('user:delete')
remove() {
return 'User deleted';
}
}
公开路由装饰器 #
创建公开装饰器 #
typescript
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
修改认证守卫 #
typescript
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
// JWT验证逻辑
return this.validateToken(context);
}
}
使用公开装饰器 #
typescript
@Controller('auth')
export class AuthController {
@Post('login')
@Public()
login() {
return 'Public login endpoint';
}
@Post('register')
@Public()
register() {
return 'Public register endpoint';
}
@Get('profile')
getProfile() {
return 'Protected profile endpoint';
}
}
组合守卫 #
typescript
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard, PermissionsGuard)
export class AdminController {
@Get('dashboard')
@Roles(Role.ADMIN)
@RequirePermissions('dashboard:read')
getDashboard() {
return 'Admin dashboard';
}
}
守卫最佳实践 #
1. 单一职责 #
每个守卫只负责一种验证:
typescript
// 认证守卫只负责认证
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// 验证用户身份
}
}
// 角色守卫只负责角色验证
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// 验证用户角色
}
}
2. 使用装饰器配置 #
typescript
@Controller('users')
@UseGuards(AuthGuard, RolesGuard)
export class UsersController {
@Get()
@Roles(Role.ADMIN)
findAll() {}
}
3. 提供清晰的错误信息 #
typescript
throw new ForbiddenException({
message: 'Access denied',
reason: 'User does not have admin role',
requiredRole: Role.ADMIN,
currentRole: user.role,
});
总结 #
本章学习了NestJS守卫:
- 守卫的概念和用途
- 认证守卫的实现
- 角色守卫的实现
- 权限守卫的实现
- 公开路由装饰器
- 守卫最佳实践
接下来,让我们学习 拦截器(Interceptor)。
最后更新:2026-03-28