NestJS — Node.js للمؤسسات

التحكّم بالوصول القائم على الأدوار (RBAC)

17 دقيقة الدرس 29 من 30

التحكّم بالوصول القائم على الأدوار (RBAC)

تُخبرك المصادقة من المستخدم؛ ويقرّر التفويض ما يُسمح له بفعله. أشيع نموذج هو RBAC — التحكّم بالوصول القائم على الأدوار — حيث للمستخدمين أدوار (مدير، محرّر، مُشاهِد) وتتطلّب المسارات أدوارًا محدّدة. يطبّق NestJS هذا بأناقة بالحُرّاس والبيانات الوصفية والمُزخرِفات التي تعرفها أصلًا.

اللبنات

يجمع RBAC في NestJS ثلاثة أشياء قابلتها في المرحلة الثالثة:

  • مُزخرِف مخصّص (@Roles()) لإعلان الأدوار التي يحتاجها مسار.
  • بيانات وصفية يُرفِقها ذلك المُزخرِف عبر SetMetadata.
  • حارس يقرأ البيانات الوصفية بـ Reflector ويفحص أدوار المستخدم.

مُزخرِف Roles

import { SetMetadata } from '@nestjs/common'; export const ROLES_KEY = 'roles'; export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);

حارس RolesGuard

يقرأ الحارس الأدوار المطلوبة ويقارنها بأدوار المستخدم المُصادَق (أرفقته استراتيجية JWT):

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { ROLES_KEY } from './roles.decorator'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const required = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [ context.getHandler(), context.getClass(), ]); if (!required) return true; // لا أدوار مطلوبة -> مفتوح const { user } = context.switchToHttp().getRequest(); return required.some((role) => user?.roles?.includes(role)); } }
getAllAndOverride تفحص المعالج والصنف المتحكّم معًا، فتتيح لـ @Roles() على مستوى الدالّة تجاوز ما على مستوى المتحكّم. وهذه الطريقة القياسية المرنة لقراءة بيانات الأدوار.

الجمع معًا

احمِ مسارًا بكلا الحارسين: أولًا صادِق (JWT)، ثم فوّض (الأدوار). الترتيب مهمّ — يجب أن تعرف من المستخدم قبل فحص أدواره:

@UseGuards(AuthGuard('jwt'), RolesGuard) @Roles('admin') @Delete(':id') remove(@Param('id') id: string) { return this.usersService.remove(id); }
سجّل RolesGuard عامًّا للاتساق. اجعله مزوّد APP_GUARD ليُفحَص كل مسار. المسارات بلا @Roles() تُسمَح ببساطة، فتشترك في الحماية لكل مسار — ولا تنسى إضافة الحارس أبدًا.

RBAC مقابل تحكّم أدقّ

RBAC بسيط ويغطّي معظم الحاجات: "المديرون يستطيعون حذف المستخدمين". لكنه يتعثّر مع قواعد تعتمد على المورد المحدّد — "يستطيع المستخدم تعديل منشوره هو لا منشورات غيره". تلك مصادقة قائمة على السمة/الملكية، وتحتاج أكثر من فحص دور.

لا تمطّ RBAC أكثر من اللازم. ترميز قواعد الملكية كأدوار أكثر تحديدًا باستمرار (editor-of-post-42) فخّ. حين تعتمد الصلاحيات على المورد وعلاقة الفاعل به، انتقل إلى نهج قائم على السياسات — التالي.

الخلاصة

يُسنِد RBAC أدوارًا للمستخدمين وأدوارًا مطلوبة للمسارات. طبّقه بمُزخرِف @Roles() (بيانات وصفية)، وحارس RolesGuard يقرؤه عبر Reflector، وحارس JWT يعمل أولًا فيُعرَف المستخدم. سجّل الحارس عامًّا واشترك بالمسارات. يعالج RBAC قواعد مستوى الأدوار جيدًا؛ ولقواعد الملكية والمورد المحدّد تحتاج سياسات — درس التفويض الأخير.