守卫与角色
使用 NestJS 守卫保护路由,并实现基于角色的访问控制(RBAC)来管理用户权限。
守卫与角色 是 CoddyKit 上的免费 Next.js 15 Fullstack (App Router + Server Actions) 课时。 这是第 4 节课,共 6 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Next.js 15 Fullstack (App Router + Server Actions) 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Next.js 15 Fullstack (App Router + Server Actions) 课程共包含 6 节课。
本课时的部分内容尚未翻译,以英文显示。
What are NestJS Guards?
In NestJS, Guards are special classes that decide if a given request should be processed by the route handler. Think of them as gatekeepers!
They sit between the incoming request and your application's logic, making authorization decisions.
- Authorization: Who is allowed to do what?
- Authentication: Who is this user? (Often handled before guards, but guards can confirm it).
Building a Basic Guard
All NestJS Guards must implement the CanActivate interface. This interface requires a single method: canActivate().
The canActivate() method returns a boolean, a Promise<boolean>, or an Observable<boolean>. If it returns true, the request proceeds; if false, it's blocked.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
// Logic to determine if user is authorized
// For now, let's just allow it
return true;
}
}A Simple Authentication Check
Let's make our AuthGuard actually do something! We'll simulate checking if a user is 'logged in' by looking for a specific header.
The ExecutionContext provides access to the request, response, and more, allowing us to inspect the incoming request.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class BasicAuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
// In a real app, you'd check JWT, session, etc.
// For this example, we check a simple header.
const hasAuthHeader = request.headers['authorization'] === 'Bearer secret-token';
return hasAuthHeader; // Only allow if header is correct
}
}Protecting Your Endpoints
To apply a guard, you use the @UseGuards() decorator. You can apply it to a single route handler or to an entire controller.
When applied to a controller, all routes within that controller will be protected by the guard.
import { Controller, Get, UseGuards } from '@nestjs/common';
import { BasicAuthGuard } from './common/guards/auth.guard';
@Controller('protected')
@UseGuards(BasicAuthGuard) // Protects all routes in this controller
export class ProtectedController {
@Get()
getProtectedData(): string {
return 'This data is protected!';
}
@Get('public')
getAnotherProtectedData(): string {
return 'More protected data.';
}
}
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ProtectedController } from './app.controller';
@Module({
controllers: [ProtectedController],
providers: [],
})
export class AppModule {}
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
console.log('App running on http://localhost:3000');
}
bootstrap();Understanding RBAC
While basic authentication checks if a user is who they say they are, Role-Based Access Control (RBAC) checks what they are allowed to do.
With RBAC, users are assigned roles (e.g., 'admin', 'editor', 'viewer'), and permissions are granted to roles, not individual users.
- Role: A collection of permissions (e.g., 'admin' can 'create', 'read', 'update', 'delete').
- User: Assigned one or more roles.
- Resource: The data or functionality being accessed.
Marking Routes with Roles
To implement RBAC, we need a way to tell our guard which roles are allowed for a specific route. NestJS allows us to create custom decorators for this!
We'll create an @Roles() decorator to attach role metadata to our route handlers.
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
// Example usage in a controller:
// @Roles('admin', 'editor')
// @Get('admin-only')
// someAdminMethod() { ... }The Roles Guard Logic
Now, let's build our RolesGuard. This guard will:
- Get the required roles from the route's metadata using the
Reflector. - Get the user's roles (e.g., from the request object after authentication).
- Compare them to see if the user has any of the required roles.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { ROLES_KEY } from '../decorators/roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true; // No roles defined, so access is allowed by default
}
const request = context.switchToHttp().getRequest();
// In a real app, 'user' would come from an authentication guard
// and contain actual user data including roles.
const user = request.user || { roles: ['viewer'] }; // Dummy user for example
const hasPermission = requiredRoles.some((role) => user.roles.includes(role));
return hasPermission;
}
}Stacking Guards for Protection
You can use multiple guards on a single route or controller. NestJS executes guards in the order they are listed in the @UseGuards() decorator.
If any guard returns false, the request is immediately blocked, and subsequent guards (and the route handler) are not executed.
- First: Authentication (Is the user logged in?)
- Second: Authorization (Does the user have the right role?)
Applying Guards and Roles
Let's see how our BasicAuthGuard, RolesGuard, and @Roles() decorator work together to protect an endpoint.
We'll simulate a user with the 'admin' role.
import { Controller, Get, UseGuards, Req } from '@nestjs/common';
import { BasicAuthGuard } from './common/guards/auth.guard';
import { RolesGuard } from './common/guards/roles.guard';
import { Roles } from './common/decorators/roles.decorator';
// Assume this comes from a real authentication process
interface User {
username: string;
roles: string[];
}
@Controller('admin')
@UseGuards(BasicAuthGuard, RolesGuard) // Guards applied in order
export class AdminController {
@Get('dashboard')
@Roles('admin') // Only users with 'admin' role can access
getAdminDashboard(@Req() req): string {
// For this runnable example's context, manually set user.
// In a real app, BasicAuthGuard would populate req.user.
req.user = { username: 'testuser', roles: ['admin'] };
return `Welcome to the Admin Dashboard, ${req.user.username}!`;
}
@Get('reports')
@Roles('admin', 'editor') // Admins or Editors can access
getReports(@Req() req): string {
req.user = { username: 'editoruser', roles: ['editor'] };
return `Accessing reports as ${req.user.username}.`;
}
}
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AdminController } from './app.controller';
@Module({
controllers: [AdminController],
providers: [],
})
export class AppModule {}
// src/main.ts (unchanged from previous runnable example)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
console.log('Admin app running on http://localhost:3000');
}
bootstrap();Guard & Role Check
Consider a NestJS route protected by @UseGuards(AuthGuard, RolesGuard) and @Roles('admin', 'moderator').
If a request comes in with a valid authentication token (passed by AuthGuard), but the authenticated user has only the role 'viewer', what will happen?
Guards & Roles Summary
Great job! In this lesson, you learned about:
- NestJS Guards: Gatekeepers that implement
CanActivateto control route access. @UseGuards(): Decorator to apply guards at controller or method level.- Role-Based Access Control (RBAC): Managing permissions based on user roles.
- Custom Decorators: Using
@SetMetadata()to attach custom data (like roles) to routes. Reflector: Used by guards to read metadata from routes.
Guards are powerful for authorization. Next, you might explore how to integrate Passport.js strategies for more robust authentication!
常见问题解答
「守卫与角色」课时是免费的吗?
是的 — 「守卫与角色」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Next.js 15 Fullstack (App Router + Server Actions) 课程的其余内容,请升级到 CoddyKit PRO。 Next.js 15 Fullstack (App Router + Server Actions) 课程共包含 6 节课。
「守卫与角色」这节课中我会学到什么?
使用 NestJS 守卫保护路由,并实现基于角色的访问控制(RBAC)来管理用户权限。 你通过在浏览器中直接运行的动手代码来练习 Next.js 15 Fullstack (App Router + Server Actions),全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Next.js 15 Fullstack (App Router + Server Actions) 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Next.js 15 Fullstack (App Router + Server Actions) 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 6 节。
「守卫与角色」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Next.js 15 Fullstack (App Router + Server Actions) 课中编写并运行代码吗?
能。每节 Next.js 15 Fullstack (App Router + Server Actions) 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。