Як реалізувати контроль доступу на основі ролей (RBAC) у NestJS?
RBAC у NestJS
Контроль доступу на основі ролей (RBAC) обмежує доступ до ресурсів на основі ролей користувачів. NestJS надає Guards та декоратори для реалізації гнучкої авторизації.
1. Визначення ролей
typescript
export enum Role {
User = 'user',
Admin = 'admin',
Moderator = 'moderator',
SuperAdmin = 'superadmin',
}2. Декоратор ролей
typescript
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);3. Guard ролей
typescript
import { CanActivate, ExecutionContext, Injectable } 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<Role[]>(
ROLES_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredRoles) return true;
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}4. Застосування Guards
typescript
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
export class AdminController {
@Get('dashboard')
@Roles(Role.Admin, Role.SuperAdmin)
getDashboard() {
return { message: 'Адміністративна панель' };
}
@Delete('users/:id')
@Roles(Role.SuperAdmin)
deleteUser(@Param('id') id: string) {
return this.usersService.delete(id);
}
@Get('stats')
@Roles(Role.Admin, Role.Moderator)
getStats() {
return this.statsService.getAll();
}
}5. Налаштування глобального Guard
typescript
@Module({
providers: [
{ provide: APP_GUARD, useClass: JwtAuthGuard },
{ provide: APP_GUARD, useClass: RolesGuard },
],
})
export class AppModule {}Розширене: Доступ на основі дозволів
Для більш точного контролю використовуйте дозволи замість (або разом) ролей:
typescript
export enum Permission {
ReadUsers = 'read:users',
WriteUsers = 'write:users',
DeleteUsers = 'delete:users',
ReadPosts = 'read:posts',
WritePosts = 'write:posts',
ManageSettings = 'manage:settings',
}
const rolePermissions: Record<Role, Permission[]> = {
[Role.User]: [Permission.ReadUsers, Permission.ReadPosts],
[Role.Moderator]: [
Permission.ReadUsers, Permission.ReadPosts, Permission.WritePosts,
],
[Role.Admin]: [
Permission.ReadUsers, Permission.WriteUsers,
Permission.ReadPosts, Permission.WritePosts,
Permission.ManageSettings,
],
[Role.SuperAdmin]: Object.values(Permission), // Усі дозволи
};typescript
export const RequirePermissions = (...permissions: Permission[]) =>
SetMetadata('permissions', permissions);
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const required = this.reflector.getAllAndOverride<Permission[]>(
'permissions',
[context.getHandler(), context.getClass()],
);
if (!required) return true;
const { user } = context.switchToHttp().getRequest();
const userPermissions = user.roles
.flatMap((role: Role) => rolePermissions[role] || []);
return required.every((perm) => userPermissions.includes(perm));
}
}Використання:
typescript
@Controller('settings')
export class SettingsController {
@Get()
@RequirePermissions(Permission.ManageSettings)
getSettings() {
return this.settingsService.getAll();
}
}Перевірка права власності на ресурс
typescript
@Injectable()
export class OwnershipGuard implements CanActivate {
constructor(private readonly postsService: PostsService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const user = request.user;
const postId = request.params.id;
if (user.roles.includes(Role.Admin)) return true;
const post = await this.postsService.findOne(postId);
return post?.authorId === user.id;
}
}
@Controller('posts')
export class PostsController {
@Put(':id')
@UseGuards(OwnershipGuard)
update(@Param('id') id: string, @Body() dto: UpdatePostDto) {
return this.postsService.update(id, dto);
}
}RBAC проти ABAC
| Особливість | RBAC | ABAC |
|---|---|---|
| Базується на | Ролях | Атрибутах (роль, час, IP тощо) |
| Складність | Проста | Складна |
| Гнучкість | Помірна | Дуже висока |
| Найкраще підходить для | Більшості веб-додатків | Підприємств, відповідності |
Найкраща практика: Починайте з простого RBAC. Додавайте перевірки на основі дозволів, коли гранулярність ролей недостатня. Додавайте guards власності для ресурсів, що належать користувачу. Використовуйте
@Public()для відкритих кінцевих точок.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.