الصلاحيات والأدوار و 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().authMiddleware.canMiddleware.@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.
