Контроль доступу на основі ролей у NestJS

pic

Реалізація контролю доступу на основі ролей (RBAC) для керування публікаціями в блозі в додатку на NestJS.

Ми створимо три ролі: Адмін, Модератор та Користувач.
Кожна роль матиме певні дозволи для створення, редагування або видалення публікацій.

Крок перший: Перерахування ролей

// enums/roles.ts  

export enum Role {  
 User = 'user',  
 Admin = 'admin',  
 Moderator = 'moderator',  
}

Крок другий: Створення власного декоратора для ролей

// decorators/role.decorator.ts  
import { SetMetadata } from '@nestjs/common';  

export const ROLES_KEY = 'roles';  
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);

Крок третій: Реалізація охоронця ролей

RolesGuard перевіряє, чи має користувач необхідні ролі для доступу до певного маршруту чи функції.

// src/guards/roles.guard.ts  

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';  
import { Reflector } from '@nestjs/core';  
import { ROLES_KEY } from '../decorators/role.decorator';  

@Injectable()  
export class RolesGuard implements CanActivate {  
 constructor(private reflector: Reflector) {}  

 canActivate(context: ExecutionContext): boolean {  
 const requiredRoles = this.reflector.getAllAndOverride(  
 ROLES_KEY,  
 [context.getHandler(), context.getClass()],  
 );  
 console.log('Required Roles:', requiredRoles);  

 if (!requiredRoles) {  
 return true;  
 }  

 const { user } = context.switchToHttp().getRequest();  
 console.log('Request User:', user);  

 if (!user || !user.role || user.role.length === 0) {  
 console.error('User or roles not defined in request or empty');  
 return false;  
 }  

 return requiredRoles.some((role) => user.role.includes(role));  
 }  
}

Крок четвертий: Реалізація охоронця для токенів доступу

import { Injectable } from '@nestjs/common';  
import { AuthGuard } from '@nestjs/passport';  

@Injectable()  
export class AccessTokenGuard extends AuthGuard('jwt') {}

Крок п’ятий: Визначення маршрутів на основі ролей

import {  
 Body,  
 Controller,  
 Delete,  
 Get,  
 Param,  
 Patch,  
 Post,  
 UseGuards,  
} from '@nestjs/common'; // Імпортуємо необхідні декоратори NestJS  
import { Roles } from 'src/common/decorators/role.decorator';  
import { Role } from 'src/common/enums/role.enum';  
import { AccessTokenGuard } from 'src/common/guards/accessToken.guard'; // Користувацький охоронець автентифікації  
import { RolesGuard } from 'src/common/guards/role.guard';  
import { CreateUserDto } from './dto/create-user.dto'; // DTO для створення користувача  
import { UpdateUserDto } from './dto/update-user.dto'; // DTO для оновлення користувача  
import { UsersService } from './users.service'; // Сервіс користувачів для обробки бізнес-логіки  

@Controller('users') // Контролер для обробки маршрутів, пов'язаних з користувачами  
export class UsersController {  
 constructor(private readonly usersService: UsersService) {} // Впровадження UsersService в контролер  

 @UseGuards(AccessTokenGuard) // Захищаємо маршрут POST за допомогою охоронця автентифікації  
 @Post() // HTTP метод POST для створення користувача  
 create(@Body() createUserDto: CreateUserDto) {  
 // Отримуємо createUserDto в тілі запиту для створення користувача  
 return this.usersService.create(createUserDto); // Делегуємо фактичну логіку в UsersService  
 }  

 @Get() // HTTP метод GET для отримання всіх користувачів  
 findAll() {  
 return this.usersService.findAll(); // Делегуємо логіку в UsersService для отримання всіх користувачів  
 }  

 @Get(':id') // HTTP метод GET для отримання користувача за його ID  
 findById(@Param('id') id: string) {  
 // Використовуємо декоратори param для доступу до ID користувача з URL  
 return this.usersService.findById(id); // Отримуємо користувача за його ID  
 }  

 @UseGuards(AccessTokenGuard, RolesGuard)  
 @Roles(Role.Admin, Role.Moderator)  
 @Patch(':id') // HTTP метод PATCH для оновлення даних користувача за його ID  
 update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {  
 // Отримуємо ID користувача та оновлені дані в тілі запиту  
 return this.usersService.update(id, updateUserDto); // Викликаємо сервіс для обробки оновлення користувача  
 }  

 @UseGuards(AccessTokenGuard, RolesGuard)  

@Roles(Role.Admin) // Захищаємо маршрут DELETE за допомогою охоронця автентифікації  
 @Delete(':id') // HTTP метод DELETE для видалення користувача за його ID  
 remove(@Param('id') id: string) {  
 // Отримуємо ID користувача з параметрів URL  
 return this.usersService.remove(id); // Делегуємо логіку видалення до сервісу  
 }  
}

Крок шостий: Отримання даних користувача з роллю з запиту

import { Injectable } from '@nestjs/common';  
import { PassportStrategy } from '@nestjs/passport';  
import { ExtractJwt, Strategy } from 'passport-jwt';  

type JwtPayload = {  
 sub: string;  
 email: string;  
 role: string;  
};  
@Injectable()  
export class AccessTokenStrategy extends PassportStrategy(Strategy, 'jwt') {  
 constructor() {  
 super({  
 jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),  
 secretOrKey: process.env.JWT_ACCESS_SECRET,  
 });  
 }  

 validate(payload: JwtPayload) {  
 return payload;  
 }  
}

Admin може створювати, редагувати та видаляти пости.

Moderator може створювати та редагувати пости.

User може переглядати пости (за замовчуванням, якщо маршрут не захищений).

Переконайтеся, що ви додаєте роль до JWT під час входу користувача або реєстрації та використовуєте цю інформацію про роль у охоронцях та декораторах.

Перекладено з: Role Base Access Control In Nest JS

Leave a Reply

Your email address will not be published. Required fields are marked *