recent
أخبار ساخنة

الصلاحيات و الأدوار Authentication في Laravel

Authorization في Laravel – الصلاحيات والأدوار وGates وPolicies

الصلاحيات والأدوار و Gates و Policies في Laravel

بعد أن تعلمنا في الدرس الثاني عشر كيفية بناء نظام Authentication وتسجيل المستخدمين وتسجيل الدخول والخروج وحماية المسارات باستخدام auth Middleware، وصلنا الآن إلى خطوة مهمة جدًا في بناء التطبيقات الحقيقية، وهي Authorization أو نظام الصلاحيات.

في الدرس السابق تعلمنا الإجابة عن السؤال:

من هو المستخدم؟

أما في هذا الدرس فسنتعلم الإجابة عن سؤال مختلف:

ماذا يُسمح لهذا المستخدم أن يفعل؟

فمثلًا، قد يكون لدينا مستخدمان مسجلان في الموقع:

Ahmed
Mohamed

كلاهما يستطيع تسجيل الدخول والوصول إلى لوحة المستخدم، لكن ربما يكون:

Ahmed = Administrator
Mohamed = User

وبالتالي قد نسمح لـ Ahmed بإدارة المستخدمين وحذف المقالات، بينما يستطيع Mohamed قراءة المقالات وإنشاء مقالاته الخاصة فقط.

وهنا يظهر مفهوم Authorization.

Laravel يوفر نظام Authorization متكاملًا يعتمد بشكل أساسي على Gates وPolicies لتنظيم قواعد السماح والمنع، ويمكن استخدام هذه القواعد داخل Controllers وRoutes وBlade وغيرها.

ما هو Authorization؟

Authorization يعني تحديد العمليات التي يُسمح للمستخدم بتنفيذها بعد أن تم التعرف على هويته.

بمعنى آخر:

Authentication
= من أنت؟

Authorization
= ماذا يمكنك أن تفعل؟

لنفترض أن المستخدم قام بتسجيل الدخول بنجاح:

Login
 ↓
Authentication
 ↓
User authenticated

هذا لا يعني تلقائيًا أنه يستطيع تنفيذ كل شيء.

بعد ذلك نحتاج إلى:

Authorization
 ↓
هل يستطيع تعديل المقال؟
هل يستطيع حذفه؟
هل يستطيع إدارة المستخدمين؟
هل يستطيع الوصول إلى لوحة الإدارة؟

الفرق بين Authentication وAuthorization

من المهم جدًا أن تحفظ هذا الفرق لأنك ستستخدم المفهومين باستمرار في Laravel.

Authentication

يتحقق من هوية المستخدم.

مثل:

هل البريد الإلكتروني وكلمة المرور صحيحتان؟
هل المستخدم مسجل الدخول؟

ونستخدم في Laravel أشياء مثل:

Auth::check();

و:

Auth::user();

Authorization

يتحقق من صلاحيات المستخدم.

مثل:

هل يستطيع هذا المستخدم حذف المقال؟
هل يستطيع تعديل هذا المقال؟
هل يستطيع فتح لوحة الإدارة؟

وبالتالي يمكن أن تكون العملية:

User
 ↓
Login
 ↓
Authentication
 ↓
Authenticated
 ↓
Authorization
 ↓
Allowed / Denied

Laravel نفسه يوضح أن Guards وProviders تخص Authentication، ولا ينبغي الخلط بينها وبين Roles وPermissions الخاصة بتحديد ما يمكن للمستخدم فعله.

مثال بسيط على Authorization

لنفترض أن لدينا مقالًا:

Article #15
Title: تعلم Laravel
Author: Ahmed

ولدينا مستخدم:

Mohamed

محمد يستطيع تسجيل الدخول، أي أن Authentication ناجح.

لكن عندما يحاول تعديل المقال:

Mohamed
   ↓
Edit Article #15
   ↓
Authorization
   ↓
هل محمد هو صاحب المقال؟
   ↓
لا
   ↓
Denied

بينما إذا كان:

Ahmed
   ↓
Edit Article #15
   ↓
Authorization
   ↓
هل Ahmed هو صاحب المقال؟
   ↓
نعم
   ↓
Allowed

وهذه هي الفكرة الأساسية التي سنبني عليها الدرس.

هل Laravel يحتوي على نظام Roles وPermissions جاهز؟

هذه نقطة مهمة جدًا.

Laravel يوفر Authorization من خلال Gates وPolicies، لكنه لا يفرض عليك نظامًا واحدًا جاهزًا لإدارة Roles وPermissions مثل:

Admin
Editor
Author
User

أو:

create-post
edit-post
delete-post
manage-users

يمكنك بناء نظام الأدوار والصلاحيات داخل تطبيقك بالطريقة المناسبة، أو استخدام حزمة خارجية متخصصة عندما يحتاج المشروع إلى نظام متقدم.

أما الأدوات الأساسية التي يوفرها Laravel نفسه فهي:

Gates
Policies
Authorization Middleware
Blade Authorization
User can()

وتوضح وثائق Laravel أن Gates مناسبة خصوصًا للقواعد العامة التي لا ترتبط مباشرةً بموديل معين، بينما Policies مناسبة لتنظيم الصلاحيات المرتبطة بـ Model أو Resource.

ما هي Gates في Laravel؟

الـ Gate عبارة عن قاعدة Authorization تحدد ما إذا كان المستخدم يستطيع تنفيذ عملية معينة.

يمكن التفكير فيها كأنها سؤال:

هل يستطيع هذا المستخدم تنفيذ هذه العملية؟

مثل:

هل يستطيع المستخدم فتح لوحة الإدارة؟

أو:

هل يستطيع المستخدم إنشاء مقال؟

أو:

هل يستطيع المستخدم حذف المقال؟

إنشاء Gate

في Laravel 13 يمكن تعريف Gates داخل AppServiceProvider باستخدام Gate Facade.

مثلًا:

<?php

namespace App\Providers;

use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Gate::define('access-admin', function (User $user) {
            return $user->is_admin;
        });
    }
}

هنا أنشأنا Gate باسم:

access-admin

ويتم التحقق من:

$user->is_admin

إذا كانت:

true

فالمستخدم يستطيع الوصول.

إذا كانت:

false

فسيتم رفض العملية.

تجربة Gate باستخدام allows

بعد إنشاء Gate يمكننا التحقق منه باستخدام:

Gate::allows('access-admin')

مثلًا:

use Illuminate\Support\Facades\Gate;

if (Gate::allows('access-admin')) {
    return 'Welcome Admin';
}

Laravel يوفر عدة طرق لفحص الصلاحيات مثل allows وdenies وauthorize.

استخدام Gate::authorize

بدل كتابة:

if (! Gate::allows('access-admin')) {
    abort(403);
}

يمكنك استخدام:

Gate::authorize('access-admin');

إذا كان المستخدم غير مصرح له، يقوم Laravel بإطلاق AuthorizationException والتي تتحول إلى استجابة HTTP من نوع 403.

مثال:

public function index()
{
    Gate::authorize('access-admin');

    return view('admin.dashboard');
}

وهذه طريقة نظيفة جدًا عندما تكون العملية يجب أن تتوقف مباشرة إذا لم يكن المستخدم مخولًا.

ما معنى HTTP 403؟

عندما ترى:

403 Forbidden

فهذا يعني أن الطلب وصل إلى التطبيق، لكن المستخدم غير مخول بتنفيذ العملية المطلوبة.

وهذا مختلف عن:

401 Unauthorized

في السياقات التي تتعلق بالمصادقة.

في تطبيق Laravel، عندما يفشل Authorization عبر Gate أو Policy بطريقة مناسبة، يمكن أن تحصل على استجابة 403.

متى نستخدم Gates؟

Gates مناسبة جدًا للعمليات العامة التي لا ترتبط بسجل Model معين.

مثل:

الوصول إلى لوحة الإدارة
الوصول إلى صفحة الإحصائيات
عرض إعدادات النظام
إدارة النظام

مثل:

Gate::define('access-admin', function (User $user) {
    return $user->is_admin;
});

لكن ماذا لو أردنا التعامل مع مقال معين؟

هنا تأتي Policies.

ما هي Policies؟

الـ Policy عبارة عن Class مخصص لتنظيم Authorization الخاص بـ Model أو Resource معين.

مثلًا لدينا:

Article

يمكننا إنشاء:

ArticlePolicy

وتضع داخلها قواعد مثل:

view
create
update
delete

Laravel يصف Policies بأنها Classes تنظم منطق Authorization حول Model أو Resource محدد.

إنشاء Policy

يمكننا استخدام Artisan:

php artisan make:policy ArticlePolicy --model=Article

سيتم إنشاء Policy داخل:

app/Policies/ArticlePolicy.php

وعند استخدام --model يمكن لـ Laravel إنشاء بنية مرتبطة بالـ Model لتسهيل كتابة قواعد Authorization.

شكل ArticlePolicy

يمكن أن تبدو Policy بهذا الشكل:

<?php

namespace App\Policies;

use App\Models\Article;
use App\Models\User;

class ArticlePolicy
{
    public function view(User $user, Article $article): bool
    {
        return true;
    }

    public function create(User $user): bool
    {
        return true;
    }

    public function update(User $user, Article $article): bool
    {
        return $user->id === $article->user_id;
    }

    public function delete(User $user, Article $article): bool
    {
        return $user->id === $article->user_id;
    }
}

لاحظ أن:

update()

تستقبل:

User $user
Article $article

وبذلك تستطيع مقارنة المستخدم بالمستخدم الذي يملك المقال.

فكرة مهمة: صاحب المورد

لنفترض أن جدول articles يحتوي على:

id
title
content
user_id

وأن:

user_id

يشير إلى صاحب المقال.

يمكننا استخدام:

return $user->id === $article->user_id;

وهذا يعني:

إذا كان المستخدم الحالي هو صاحب المقال
→ يسمح له بالتعديل

إذا لم يكن صاحب المقال
→ يمنع من التعديل

Policy Method للتعديل

يمكن أن يكون لدينا:

public function update(User $user, Article $article): bool
{
    return $user->id === $article->user_id;
}

هذه القاعدة تقول:

المستخدم يستطيع تعديل المقال إذا كان هو صاحبه.

Policy Method للحذف

وبالمثل:

public function delete(User $user, Article $article): bool
{
    return $user->id === $article->user_id;
}

وبذلك لا يستطيع مستخدم عادي حذف مقال مستخدم آخر.

ماذا لو كان المدير يستطيع تعديل كل المقالات؟

هنا يمكننا إضافة Role بسيط للمستخدم.

مثلًا:

users

id
name
email
password
role

وتكون قيمة:

admin

أو:

editor

أو:

user

يمكن أن تصبح Policy:

public function update(User $user, Article $article): bool
{
    return $user->role === 'admin'
        || $user->id === $article->user_id;
}

الآن:

Admin
 ↓
يستطيع تعديل أي مقال

User
 ↓
يستطيع تعديل مقاله فقط

وهذا مثال بسيط جدًا على دمج Roles مع Policies.

لماذا لا نضع هذه الشروط داخل Controller؟

قد يفكر المبتدئ في كتابة:

public function update(Article $article)
{
    if (
        auth()->user()->role !== 'admin'
        && auth()->id() !== $article->user_id
    ) {
        abort(403);
    }

    // update...
}

قد يعمل هذا الكود، لكن المشكلة تظهر عندما يصبح التطبيق كبيرًا.

قد تضطر إلى تكرار نفس الشرط في:

ArticleController
CommentController
AdminController
Blade
API
Tests

وبمرور الوقت تصبح قواعد الصلاحيات موزعة في أماكن كثيرة.

هنا تظهر أهمية Policy.

بدل تكرار القاعدة:

من يستطيع تعديل المقال؟

نضعها في مكان واحد:

ArticlePolicy

وهذا يجعل الكود أكثر تنظيمًا وأسهل في الاختبار والصيانة.

ربط Policy مع Model

Laravel 13 يستطيع اكتشاف Policies تلقائيًا عندما تتبع أسماء الملفات والمجلدات conventions المعتادة.

مثلًا:

app/Models/Article.php
app/Policies/ArticlePolicy.php

بحيث يتوافق اسم:

Article

مع:

ArticlePolicy

وتوضح وثائق Laravel أن Policy Discovery يعتمد على هذه conventions، ويمكن أيضًا تسجيل Policy يدويًا عند الحاجة.

استخدام Policy من خلال User Model

بعد إعداد Policy، يمكننا استخدام:

$user->can('update', $article)

مثلًا:

if ($user->can('update', $article)) {
    // يستطيع التعديل
}

أو باستخدام المستخدم الحالي:

if (auth()->user()->can('update', $article)) {
    // يسمح بالتعديل
}

وهذا يجعل قراءة الكود سهلة جدًا:

هل يستطيع هذا المستخدم تحديث هذا المقال؟

استخدام can داخل Controller

لنفترض أن لدينا:

public function update(Article $article)
{
    if (! auth()->user()->can('update', $article)) {
        abort(403);
    }

    // Update article...
}

يمكن أن تعمل هذه الطريقة.

لكن Laravel يوفر طريقة أنظف باستخدام:

authorize()

أو Gate.

استخدام authorize داخل Controller

يمكنك استخدام:

$this->authorize('update', $article);

مثلًا:

public function update(Article $article)
{
    $this->authorize('update', $article);

    // Update article...
}

إذا لم يكن المستخدم مخولًا، يتم إيقاف العملية والاستجابة بحالة Authorization المناسبة.

استخدام Policy مع Resource Controller

وهنا نعود إلى ما تعلمناه في الدرس الخامس.

إذا كان لدينا:

Route::resource('articles', ArticleController::class);

فيمكننا تنظيم العمليات:

index
create
store
show
edit
update
destroy

مع Policy:

viewAny
view
create
update
delete

مثلًا:

ArticleController@update
        ↓
ArticlePolicy@update
        ↓
Allowed / Denied

وهذه بنية ممتازة لتطبيقات CRUD.

Authorization باستخدام Middleware

يمكن أيضًا تطبيق Authorization على Route باستخدام Middleware.

مثلًا يمكن استخدام:

Route::put('/articles/{article}', [ArticleController::class, 'update'])
    ->middleware('can:update,article');

الفكرة هنا أن Laravel سيتحقق من قدرة المستخدم على:

update

للمقال الموجود في:

article

قبل تنفيذ Controller.

وهذا مفيد عندما تريد وضع شرط الصلاحية مباشرةً على Route.

الفرق بين auth وcan Middleware

في الدرس السابق تعلمنا:

->middleware('auth')

وهذا يعني:

هل المستخدم مسجل الدخول؟

أما:

->middleware('can:update,article')

فالمعنى هو:

هل المستخدم مخول بتحديث هذا المقال؟

إذن:

auth
 ↓
Authentication

can
 ↓
Authorization

Authorization داخل Blade

من المهم ألا تعرض أزرار العمليات التي لا يستطيع المستخدم تنفيذها.

مثلًا:

@can('update', $article)
    <a href="{{ route('articles.edit', $article) }}">
        تعديل
    </a>
@endcan

إذا كان المستخدم يستطيع التعديل، يظهر الزر.

إذا لم يكن يستطيع، لا يظهر.

زر الحذف

يمكن أيضًا:

@can('delete', $article)
    <form method="POST" action="{{ route('articles.destroy', $article) }}">
        @csrf
        @method('DELETE')

        <button type="submit">
            حذف
        </button>
    </form>
@endcan

هذا يجعل الواجهة أكثر ذكاءً.

لكن انتبه إلى نقطة مهمة:

إخفاء الزر ليس حماية كافية.

حتى لو لم يظهر زر Delete، يمكن للمستخدم محاولة إرسال Request يدويًا إلى Route.

لذلك يجب أن تكون Policy أو Authorization موجودة على الخادم أيضًا.

@cannot و@canany

يمكن استخدام:

@cannot('update', $article)
    <p>لا يمكنك تعديل هذا المقال.</p>
@endcannot

كما يمكن التحقق من عدة صلاحيات باستخدام:

@canany(['update', 'delete'], $article)
    <p>لديك صلاحية لإدارة المقال.</p>
@endcanany

Laravel يوفر توجيهات Blade مخصصة لفحص Authorization مثل @can و@cannot و@canany.

بناء نظام Roles بسيط

لنفترض أن مشروعنا يحتاج إلى ثلاثة أدوار:

admin
editor
user

يمكن إضافة عمود إلى جدول users.

ننشيء Migration:

php artisan make:migration add_role_to_users_table --table=users

ثم:

Schema::table('users', function (Blueprint $table) {
    $table->string('role')->default('user');
});

بعد ذلك:

php artisan migrate

الآن كل مستخدم يمكن أن يمتلك:

role = user

أو:

role = editor

أو:

role = admin

التحقق من Role

يمكنك كتابة:

if (auth()->user()->role === 'admin') {
    // Admin
}

لكن الأفضل مع نمو التطبيق أن تجعل هذا المنطق أكثر تنظيمًا.

مثلًا داخل User Model:

public function isAdmin(): bool
{
    return $this->role === 'admin';
}

ثم:

if (auth()->user()->isAdmin()) {
    // Admin
}

هذا يجعل قراءة الكود أسهل.

استخدام Role داخل Gate

يمكننا الآن بناء Gate:

Gate::define('access-admin', function (User $user) {
    return $user->isAdmin();
});

ثم في Controller:

Gate::authorize('access-admin');

أصبح لدينا:

User
 ↓
role
 ↓
isAdmin()
 ↓
Gate
 ↓
Admin Dashboard

Roles وPolicies معًا

في تطبيق المقالات، يمكن أن تكون القاعدة:

Admin
→ يستطيع إدارة جميع المقالات

Editor
→ يستطيع تعديل المقالات

Author
→ يستطيع تعديل مقالاته

User
→ يستطيع القراءة فقط

ويمكن أن تكون Policy:

public function update(User $user, Article $article): bool
{
    if ($user->role === 'admin') {
        return true;
    }

    if ($user->role === 'editor') {
        return true;
    }

    return $user->id === $article->user_id;
}

هذه طريقة بسيطة لفهم العلاقة بين:

Role
+
Resource
+
Action

ماذا لو أصبح المشروع كبيرًا؟

في المشاريع الكبيرة قد يصبح لديك عشرات الصلاحيات:

articles.create
articles.update
articles.delete

users.view
users.create
users.update
users.delete

categories.create
categories.update
categories.delete

وقد يكون المستخدم لديه أكثر من Role.

مثلًا:

Ahmed
Roles:
- editor
- author

هنا قد يكون من الأفضل تصميم نظام أكثر مرونة باستخدام جداول مثل:

users
roles
permissions
role_user
permission_role

أو استخدام حزمة متخصصة لإدارة Roles وPermissions.

الفكرة المهمة في هذا الدرس هي فهم Authorization في Laravel، وليس بناء نظام Enterprise كامل لإدارة الصلاحيات.

Policy أم Gate؟

هذه من أهم النقاط في الدرس.

استخدم Gate عندما تكون القاعدة عامة

مثل:

هل يستطيع المستخدم فتح لوحة الإدارة؟

يمكن:

Gate::define('access-admin', ...);

استخدم Policy عندما تكون القاعدة مرتبطة بـ Model

مثل:

هل يستطيع المستخدم تعديل هذا المقال؟

استخدم:

ArticlePolicy

لأن العملية مرتبطة بـ:

Article

وتلخص وثائق Laravel الفرق بأن Gates مناسبة لقواعد Authorization العامة، بينما Policies تنظّم قواعد مرتبطة بموديل أو Resource معين.

مثال عملي كامل

لنقم ببناء نظام بسيط للمقالات.

لدينا:

User
Article

والمقال يحتوي على:

id
title
content
user_id

لدينا أيضًا:

users.role

وقيمته:

admin
user

إنشاء Policy

نفذ:

php artisan make:policy ArticlePolicy --model=Article

ثم:

<?php

namespace App\Policies;

use App\Models\Article;
use App\Models\User;

class ArticlePolicy
{
    public function update(User $user, Article $article): bool
    {
        return $user->role === 'admin'
            || $user->id === $article->user_id;
    }

    public function delete(User $user, Article $article): bool
    {
        return $user->role === 'admin'
            || $user->id === $article->user_id;
    }
}

الآن لدينا قاعدة واضحة:

Admin
→ Update any article
→ Delete any article

Owner
→ Update own article
→ Delete own article

Other user
→ Cannot update
→ Cannot delete

Controller

داخل:

ArticleController

يمكننا:

public function update(Request $request, Article $article)
{
    $this->authorize('update', $article);

    $validated = $request->validate([
        'title' => ['required', 'string', 'max:255'],
        'content' => ['required', 'string'],
    ]);

    $article->update($validated);

    return redirect()
        ->route('articles.show', $article)
        ->with('success', 'تم تحديث المقال بنجاح.');
}

لاحظ ترتيب العملية:

Request
 ↓
Authorization
 ↓
Validation
 ↓
Update
 ↓
Redirect

وهذا أفضل من تحديث البيانات قبل التحقق من الصلاحية.

Blade

في صفحة المقال:

@can('update', $article)
    <a href="{{ route('articles.edit', $article) }}">
        تعديل المقال
    </a>
@endcan

@can('delete', $article)
    <form method="POST" action="{{ route('articles.destroy', $article) }}">
        @csrf
        @method('DELETE')

        <button type="submit">
            حذف المقال
        </button>
    </form>
@endcan

الآن تظهر العمليات المناسبة للمستخدم.

ماذا يحدث عند محاولة الوصول بشكل مباشر؟

لنفترض أن مستخدمًا غير مصرح له حاول الوصول إلى:

/articles/15/edit

حتى لو لم يظهر له زر التعديل، يمكنه كتابة الرابط يدويًا.

لكن Controller يستخدم:

$this->authorize('update', $article);

وبالتالي:

Request
 ↓
Controller
 ↓
Policy
 ↓
Denied
 ↓
403 Forbidden

وهذه هي الحماية الحقيقية.

Gate Responses

في بعض الحالات قد لا تريد مجرد:

true
false

بل تريد رسالة توضّح سبب الرفض.

Laravel يسمح بإرجاع Response من Gate، بحيث يمكن تحديد سبب الرفض.

مثلًا:

use Illuminate\Auth\Access\Response;

Gate::define('access-admin', function (User $user) {
    return $user->isAdmin()
        ? Response::allow()
        : Response::deny('يجب أن تكون مديرًا للوصول إلى هذه الصفحة.');
});

وهذا مفيد عندما تريد تقديم سبب واضح للمنع.

Laravel 13 وPHP Attributes في Authorization

من الإضافات المهمة في Laravel 13 توسيع استخدام PHP Attributes، ومنها #[Authorize] في Controllers.

مثلًا يمكن تعريف Authorization مباشرةً على Method:

use Illuminate\Routing\Attributes\Controllers\Authorize;

#[Authorize('delete', 'article')]
public function destroy(Article $article)
{
    // ...
}

وتوضح ملاحظات إصدار Laravel 13 أن #[Authorize] يمكن استخدامها لتطبيق Policy checks مباشرة على Controller methods.

هذه الطريقة مفيدة عندما تريد كتابة قواعد Middleware وAuthorization بطريقة declarative بالقرب من Controller Method.

لكن كمبتدئ، من الأفضل أولًا فهم:

$this->authorize()

و:

Gate

و:

Policy

قبل الانتقال إلى الأساليب المتقدمة.

Authorization داخل Form Request

في الدرس السابع تعلمنا Form Requests.

ومن المفيد معرفة أن Form Request يحتوي أيضًا على:

authorize()

يمكن استخدامه للتحقق من أن المستخدم مسموح له بتنفيذ الطلب قبل تنفيذ منطق الـ Controller. وتوضح وثائق Laravel أن authorize() في Form Request يمكنها استخدام المستخدم الحالي وGates أو Policies لاتخاذ قرار السماح.

مثلًا:

public function authorize(): bool
{
    $article = Article::find($this->route('article'));

    return $article
        && $this->user()->can('update', $article);
}

وهكذا يصبح Form Request مسؤولًا عن:

Authorization
+
Validation

في مكان واحد.

Authorization مع الاختبارات

بعد بناء نظام الصلاحيات يجب أن نختبره.

يمكن أن يكون لدينا اختبار يتأكد من أن صاحب المقال يستطيع التعديل، بينما مستخدم آخر لا يستطيع.

Laravel يوفر actingAs() داخل HTTP Tests لتسجيل مستخدم بشكل مصادق عليه أثناء الاختبار.

مثلًا:

$user = User::factory()->create();

$response = $this->actingAs($user)
    ->get('/dashboard');

وفي اختبارات Authorization يمكننا إنشاء مستخدمين مختلفين واختبار السيناريوهات.

مثل:

Admin → Allowed
Owner → Allowed
Other User → Forbidden
Guest → Redirect to Login

أخطاء شائعة في Authorization

الاعتماد على إخفاء الأزرار فقط

هذا خطأ.

@if ($user->isAdmin())
    <button>Delete</button>
@endif

هذا يحسن الواجهة، لكنه لا يحمي Route وحده.

يجب أن تكون الحماية على الخادم أيضًا

الخلط بين auth وauthorization

وجود:

->middleware('auth')

يعني أن المستخدم مسجل الدخول.

لا يعني أنه يستطيع تنفيذ كل العمليات.

قد تحتاج إلى:

auth
+
Policy

وضع كل الصلاحيات داخل Controller

عندما يصبح Controller مليئًا بشروط مثل:

if (...)

قد يكون الوقت مناسبًا لنقل قواعد Authorization إلى Policies أو Gates.

تكرار نفس القواعد

إذا وجدت نفس الشرط يتكرر في عدة Controllers، فهذه إشارة إلى أن القاعدة قد تكون أفضل داخل Policy أو Gate.

عدم اختبار المستخدم غير المصرح له

لا تختبر فقط:

Admin

اختبر أيضًا:

Guest
User
Owner
Other User
Admin

بحسب تصميم التطبيق.

أفضل الممارسات

استخدم Authentication وAuthorization بشكل منفصل

فكر دائمًا:

Authentication
→ من أنت؟

Authorization
→ ماذا يمكنك أن تفعل؟

استخدم Policies مع Models

إذا كانت القاعدة مرتبطة بـ:

Article
Comment
Order
Invoice

فغالبًا ستكون Policy مناسبة.

استخدم Gates للقواعد العامة

مثل:

access-admin
view-reports
manage-settings

لا تعتمد على الواجهة فقط

إخفاء الزر لا يعني أن العملية محمية.

الحماية الحقيقية يجب أن تكون في Backend.

اجعل الصلاحيات قابلة للاختبار

كل قاعدة Authorization مهمة يجب أن يكون من السهل اختبارها.

تمرين عملي

حان الآن دورك لتطبيق ما تعلمته.

أنشئ نظام مقالات يحتوي على:

Admin
Author
User

واجعل لكل مستخدم Role.

المطلوب

المهمة الأولى

أضف عمود:

role

إلى جدول users.

المهمة الثانية

اجعل القيم الممكنة:

admin
author
user

المهمة الثالثة

أنشئ:

php artisan make:policy ArticlePolicy --model=Article

المهمة الرابعة

اجعل:

Admin

يستطيع تعديل وحذف أي مقال.

المهمة الخامسة

اجعل:

Author

يستطيع تعديل وحذف مقالاته فقط.

المهمة السادسة

اجعل:

User

لا يستطيع تعديل أو حذف المقالات.

المهمة السابعة

استخدم:

$this->authorize('update', $article);

داخل Controller.

المهمة الثامنة

استخدم:

@can('update', $article)

لإظهار زر التعديل.

المهمة التاسعة

اختبر الحالات التالية:

Guest
 ↓
لا يستطيع تعديل المقال

User
 ↓
لا يستطيع تعديل المقال

Author
 ↓
يستطيع تعديل مقاله

Author
 ↓
لا يستطيع تعديل مقال Author آخر

Admin
 ↓
يستطيع تعديل أي مقال

إذا تمكنت من تنفيذ هذه الحالات، فأنت بدأت بالفعل في بناء نظام Authorization عملي في Laravel.

ماذا تعلمنا في هذا الدرس؟

في هذا الدرس انتقلنا من Authentication إلى Authorization.

تعلمنا أن:

Authentication
= من أنت؟

Authorization
= ماذا يسمح لك أن تفعل؟

وتعرفنا على:

  • Authorization.
  • Gates.
  • Policies.
  • Roles.
  • Permissions كمفهوم.
  • Gate::allows().
  • Gate::authorize().
  • $user->can().
  • $this->authorize().
  • auth Middleware.
  • can Middleware.
  • @can.
  • @cannot.
  • @canany.
  • Policy Methods.
  • حماية Models.
  • حماية المقالات حسب المالك.
  • السماح للـ Admin بإدارة الموارد.
  • استخدام Form Request في Authorization.
  • اختبار الصلاحيات.
  • PHP Attribute #[Authorize] في Laravel 13.

بعد أن تعلمنا Authentication في الدرس السابق، أصبح لدينا الآن الجزء الثاني من نظام المستخدمين.

يمكن تلخيص ما تعلمناه حتى الآن بالشكل التالي:

User
 ↓
Register
 ↓
Login
 ↓
Authentication
 ↓
Session
 ↓
Authorization
 ↓
Gate / Policy
 ↓
Role / Permission
 ↓
Allowed / Denied

في تطبيق حقيقي، لا يكفي أن يعرف Laravel أن المستخدم قام بتسجيل الدخول.

يجب أيضًا أن يعرف ما إذا كان هذا المستخدم مخولًا لتنفيذ العملية التي يطلبها.

فمثلًا:

Ahmed
Role: admin

يمكنه:

إدارة المستخدمين
إدارة المقالات
حذف المقالات

بينما:

Mohamed
Role: author

يمكنه:

إنشاء المقالات
تعديل مقالاته
حذف مقالاته

وهكذا يصبح التطبيق أكثر تنظيمًا وأمانًا.

وأهم قاعدة يجب أن تتذكرها هي:

Authentication
→ هل المستخدم معروف ومسجل الدخول؟

Authorization
→ هل هذا المستخدم مسموح له بتنفيذ العملية؟

في الدرس القادم سننتقل إلى موضوع مهم جدًا في التطبيقات التي تعتمد على الملفات والصور، وهو File Storage ورفع الملفات في Laravel، وسنتعلم كيفية رفع الصور والملفات وتخزينها والتحقق منها وعرضها داخل التطبيق، مع ربط ذلك بما تعلمناه عن Requests وValidation وModels.

google-playkhamsatmostaqltradentX