یه تابستون متفاوت با یه تصمیم هوشمندانه! دوره هوش مصنوعی یه تابستون متفاوت با یه تصمیم هوشمندانه! دوره هوش مصنوعی
🎯 ثبت نام
بستن تبلیغات
دوره آموزش جامع PHP از صفر تا صد با پروژه‌های عملی

توی دوره رایگان PHP، هر چیزی که برای حرفه‌ای شدن نیاز داری رو یاد می‌گیری! از مفاهیم پایه تا پیشرفته، همراه با یه پروژه واقعی برای ساخت یه سایت مثل آپارات.

مشاهده بیشتر
دوره پروژه‌محور لاراول: ساخت وبسایت خبری از صفر تا صد

توی این دوره با هم یه وبسایت خبری واقعی رو از صفر می‌سازیم! از طراحی دیتابیس و احراز هویت تا ساخت API و یه پنل مدیریت حرفه‌ای، همه رو یاد می‌گیریم و آماده پروژه‌های واقعی میشی!

مشاهده بیشتر

آموزش تخصیص مجوز (Authorization) در لاراول

تخصیص مجوز (Authorization)

  1. مقدمه
  2. تعریف و ایجاد ability ها
  3. بررسی ability ها
    • بررسی ability ها از طریق Gate Facade
    • بررسی از طریق مدل User
    • داخل قالب های Blade
    • درون درخواست های فرم (form requests)
  4. سیاست های تخصیص مجوز (Policy ها)
    • ایجاد policy ها
    • نوشتن policy های جدید
    • بررسی policy ها
  5. تخصیص مجوز در کنترلر

مقدمه

علاوه بر سرویس های احراز هویت (authentication) که با نصب لاراول آماده و تنظیم شده در اختیار شما قرار می گیرد، این فریم ورک همچنین روشی آسان برای سازمان دهی منطق تخصیص مجوز (authorization) و کنترل دسترسی به منابع فراهم می نماید. متدها و توابع کمکی فراوانی وجود دارد که در سازمان دهی منطق تخصیص مجوز به شما کمک می کند. در این مبحث به شرح همگی آن ها خواهیم پرداخت.

تعریف و ایجاد ability ها

در لاراول، شما می توانید با تعریف یک ability به وسیله ی کلاس Illuminate\Auth\Access\Gate بررسی کنید آیا مجوز انجام عملیات خاصی به کاربر داده شده یا خیر (کاربر قادر به انجام عملیات خاصی مثلا بروز آوری مدل است یا خیر). AuthServiceProvider که به صورت پیش فرض همراه با چارچوب نرم افزاری لاراول در اختیار توسعه دهنده قرار می گیرد مکان مناسبی برای ثبت و ایجاد تمامی ability های مورد نیاز برنامه می باشد. در اینجا به منظور آموزش، یک ابیلیتی به نام update-post نوشته که User جاری و مدلPost را به عنوان پارامتر دریافت می کند. داخل این ability بررسی می کنیم آیا id کاربر با user_id مدل Post مطابقت دارد یا خیر:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
namespace App\Providers;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
     
   public function boot(GateContract $gate)
   {
       $this->registerPolicies($gate);
  
       $gate->define('update-post', function ($user, $post) {
           return $user->id === $post->user_id;
       });
   }
}  
<button></button>

همان طور که در این مثال می بینید ما بررسی نکردیم که آیا متغیر$user تهی / NULL هست یا خیر. در صورت عدم وجود کاربر احراز هویت شده یا هر کاربر دیگری که به وسیله ی متد forUser مشخص شده باشد، Gate به صورت خودکار مقدار بولی false را برای تمامی ability ها برمی گرداند.

Ability های مبتنی بر کلاس

علاوه بر معرفی Closure ها به صورت توابع بازفراخوان (authorization callbacks)، می توان متدهای کلاس را با ارسال یک رشته حاوی اسم کلاس و متد (به عنوان پارامتر )، ثبت و معرفی نمود. کلاس مورد نظر در زمان نیاز توسط service container در دسترس قرار داده می شود (resolve می شود).
برای تعریف یک ability از کلاس Gate استفاده می کنیم و سپس (به وسیله ی تابع define) اسم ability را به عنوان آرگومان اول و تابع Closure(به عنوان تابع callback ای که مجوزدهی را انجام می دهد) را به عنوان پارامتر دوم به تابع define پاس می دهیم:

1
2
                     $gate->define('update-post', 'Class@method');
<button></button>

ایجاد وقفه در Authorization check ها (Intercepting Authorization Checks)

گاهی لازم می شود تمامی ability های ممکن (عملیاتی که کابر می تواند یا اجازه ی انجام آن ها را دارد) را به یک کاربر معین اعطا کنید. برای این منظور کافی است متد before را صدا زده و یک callback به آن پاس دهید که قبل از تمامی دیگر authorization check ها اجرا می شود:

1
2
3
4
5
6
$gate->before(function ($user, $ability) {
   if ($user->isSuperAdmin()) {
       return true;
   }
});
<button></button>

در صورتی که تابع بازفراخوان (callback) before به عنوان خروجی یک نتیجه ی non-null برگرداند، آن خروجی نتیجه ی نهایی authorization check (پروسه ی بررسی مجوز) محسوب می شود. می توانید از یک متد after برای تعریف یک تابع بازفراخوان کمک بگیرید که پس از هر بار authorization check (بررسی مجوز) اجرا می شود. لازم است دقت داشته باشید که امکان ویرایش نتیجه ی authorization check (پروسه ی بررسی مجوز) از تابع after وجود ندارد:

1
2
3
4
$gate->after(function ($user, $ability, $result, $arguments) {
   //
});
<button></button>

چک کردن ability ها

از طریق Gate Facade

پس از ایجاد ability مورد نظر، می توان آن را به شیوه های مختلف چک کرد. اولین روش در استفاده از تابع check، allows یا denies در فاسادGate خلاصه می شود. تمامی متدهای یاد شده اسم ability و نیز آرگومان هایی که بایستی به تابع بازفراخوان (callback) پاس داده شود را به عنوان پارامتر ورودی دریافت می کنند. توجه داشته باشید که لزومی ندارد user جاری را به این توابع به عنوان آرگومان ارسال کنید. زیرا کلاسGate خود، user را به آرگومان های پاس داده شده به تابع بازفراخوان (callback) ضمیمه می کند. بنابراین به هنگام چک کردن و بررسی ابیلیتیupdate-post که در بالا آن را ایجاد کردیم، فقط نمونه ی Post را به متد denies به عنوان آرگومان ارسال کنیم:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
namespace App\Http\Controllers;
use Gate;
use App\User;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
     
   public function update($id)
   {
       $post = Post::findOrFail($id);
       if (Gate::denies('update-post', $post)) {
           abort(403);
       }
       // Update Post...
   }
}
<button></button>

متد allows دقیقا عکس denies عمل می کند بدین معنی که اگر action مجوزدهی شد در آن صورت مقدار بولی true را بازیابی می کند. متدcheck در واقع یک alias یا نام مستعار برای متد allows محسوب می شود.

بررسی برخوردار بودن کاربر از ability معین

می توان به وسیله ی فاساد Gate بررسی کرد آیا کاربری به غیر از کاربر سنجش هویت شده دارای ability (توان و مجوز لازم برای انجام عملیات) خاصی هست یا خیر. برای این منظور تنها کافی است تابع forUser را به صورت زیر فراخوانی نمایید:

1
2
3
4
if (Gate::forUser($user)->allows('update-post', $post)) {
   //
}
<button></button>

ارسال چندین آرگومان به بازفراخوان ها

توابع Callback یک ability می توانند به صورت همزمان چندین آرگومان دریافت کنند:

1
2
3
4
Gate::define('delete-comment', function ($user, $post, $comment) {
   //
});
<button></button>

چنانچه ability شما به چندین آرگومان نیاز داشته باشد، در آن صورت می توانید آرگومان های خود را در قالب یک آرایه به آن پاس دهید:

1
2
3
4
if (Gate::allows('delete-comment', [$post, $comment])) {
   //
}
<button></button>

بررسی از طریق مدل User

روش دیگر این است که ability های خود را از طریق نمونه ی مدل User بررسی نمایید. خود لاراول به صورت پیش فرض از یک trait به نامAuthorizable استفاده می کند که در کل دو متد در اختیار ما قرار می دهد: can و cannot. این دو متد را می توان مشابه توابع allows وdenies موجود در Gate facade مورد استفاده قرار داد. بنابراین کد برنامه ی خود را با توجه به مثال های قبلی به صورت زیر ویرایش می کنیم:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace App\Http\Controllers;
use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
     
   public function update(Request $request, $id)
   {
       $post = Post::findOrFail($id);
       if ($request->user()->cannot('update-post', $post)) {
           abort(403);
       }
       // Update Post...
   }
}
<button></button>

بدیهی است که متد can نقطه ی مقابل متد cannot می باشد:

1
2
3
4
if ($request->user()->can('update-post', $post)) {
   // Update Post...
}
<button></button>

داخل قالب های Blade

برای آسانی بیشتر، لارول متد @can از توابع (موتور ایجاد قالب) Blade را در اختیار شما قرار می دهد. به وسیله ی دستور ذکر شده می توان به سرعت بررسی کرد آیا کاربر احراز هویت شده جاری از ability (قابلیت انجام عملیات ) خاصی برخوردار می باشد یا خیر. مثال:

1
2
3
4
5
<a href="/post/{{ $post->id }}">View Post</a>
@can('update-post', $post)
                <a href="/post/{{ $post->id }}/edit">Edit Post</a>
@endcan
<button></button>

البته می توان دستور @can را همراه با دستور @else بکار برد:

1
2
3
4
5
6
@can('update-post', $post)
   <!-- The Current User Can Update The Post -->
@else
   <!-- The Current User Can't Update The Post -->
@endcan
<button></button>

درون درخواست های فرم (form request)

می توانید ability های تعریف شده در کلاس Gate را از متد authorize یک form request همچون post گرفته و استفاده نمایید:

1
2
3
4
5
6
public function authorize()
{
   $postId = $this->route('post');
   return Gate::allows('update', Post::findOrFail($postId));
}
<button></button>

سیاست های تخصیص مجوز (Policy ها)

ایجاد policy ها

از آنجایی که تعریف کل منطق مجوز دهی (authorization) در AuthServiceProvider ممکن است در برنامه های بسیار بزرگ ملال آور و پر زحمت شود، لاراول به شما این امکان را می دهد تا منطق authorization را به کلاس های "Policy" تقسیم نمایید. policy ها در واقع کلاس های ساده یPHP هستند که منطق authorization را بر اساس منابعی که دسترسی به آن ها را مجوزدهی می کنند، گروه بندی می نمایند.
در گام نخست یک policy تعریف می کنیم که دسترسی به مدل Post را مجوزدهی می نماید. برای تعریف یک policy جدید کافی است دستور آرتیزانmake:policy را اجرا نمایید. policy ای که به واسطه ی این دستور ایجاد می شود در پوشه ی app/Policies قرار می گیرد:

1
2
                    php artisan make:policy PostPolicy
<button></button>

ثبت Policy ها

پس از اینکه policy مورد نظر ایجاد شد، بایستی آن را در کلاس Gate ثبت نمایید. AuthServiceProvider دربردارنده ی یک خاصیت (property) به نام policies می باشد که entity های مختلف را به policy هایی که آن ها را مدیریت می کنند، نگاشت (map) می نماید. در مثال زیر مشخص می کنیم که کلاس PostPolicy سیاست تخصیص مجوز/policy مدل Post باشد:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace App\Providers;
use App\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
    
   protected $policies = [
       Post::class => PostPolicy::class,
   ];
    
   public function boot(GateContract $gate)
   {
      $this->registerPolicies($gate);
   }
}
<button></button>

نوشتن policy ها (سیاست های تخصیص مجوز)

پس از اینکه policy ایجاد شده و در کلاس Gate به ثبت رسید، می توان به ازای هر ability ای که policy مجوزدهی می کند، متدهایی را درنظر گرفته و اضافه کنیم. به عنوان نمونه، یک متد update در PostPolicy تعریف می کنیم که بررسی می کند آیا User مورد نظر می تواند یک Post را بروز آوری ("update") کند یا خیر:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
namespace App\Policies;
use App\User;
use App\Post;
class PostPolicy
{
    
   public function update(User $user, Post $post)
   {
       return $user->id === $post->user_id;
   }
}
<button></button>

به همین ترتیب می توانید به ازای ability هایی که policy مجوزدهی می کند، متدهای دیگری تعریف کنید. به عنوان مثال می توانید متدهای show،define یا addComment را برای مجوزدهی به action های مختلف Post تعریف نمایید.

نکته:

تمامی policy ها توسط service container ارائه می شوند، بدین معنی که می توانید کلیه ی dependency های موردنیاز را در تابعconstructor کلاس policy اعلان نوع (type-hint) نمایید. خواهید دید که dependency ها خود به صورت خودکار تزریق می شوند.

intercept کردن (ایجاد وقفه در) تمامی پروسه های بررسی

گاهی لازم می شود در policy، تمامی ability ها را به یک کاربر اعطا کنید. برای این منظور کافی است متد before را در این policy تعریف نمایید. این متد، همان طور که از نامش پیداست، پیش از تمامی دیگر فرایند های بررسی مجوز (authorization check) در policy اجرا می شود:

1
2
3
4
5
6
7
public function before($user, $ability)
{
   if $user->isSuperAdmin()) {
       return true;
   }(
}
<button></button>

چنانچه خروجی متد before مقداری غیر null بود، آن خروجی نتیجه ی نهایی پروسه ی بررسی مجوز در نظر گرفته خواهد شد.

بررسی policy ها

متدهای policy درست به شیوه ی توابع بازفراخوان مجوزدهی (authorization callbacks) مبتنی بر Closure، صدا زده می شوند. شما می توانید از فاساد Gate، مدل User، دستور @can از توابع Blade یا تابع کمکی (helper) policy استفاده نمایید.

از طریق فاساد Gate

Gate با بررسی کلاس آرگومان های ارسالی به متدهایش تشخیص می دهد کدام policy را بایستی برای تخصیص مجوز مورد استفاده قرار دهد. بر این اساس اگر یک نمونه از post را به متد denies پاس دهیم، Gate برای مجوزدهی به action ها از PostPolicy مربوطه استفاده می کند:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
namespace App\Http\Controllers;
use Gate;
use App\User;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
    
   public function update($id)
   {
       $post = Post::findOrFail($id);
  
       if (Gate::denies('update', $post)) {
           abort(403);
       }
  
       // Update Post...
   }
}
<button></button>

از طریق مدل User

توابع can و cannot از مدل User نیز به شیوه ی ذکر شده در بالا، تشخیص می دهند که بایستی از کدام policy ها برای مجوزدهی استفاده کنند (اگر policy برای آرگومان ارسالی به دو تابع ذکر شده در دسترس باشد، این دو تابع به صورت خودکار از آن برای تخصیص مجوز استفاده می کنند). متدهای ذکر شده به شما اجازه می دهند تا به راحتی action های لازم برای نمونه ی User (که توسط اپلیکیشن بازیابی می شود) را مجوزدهی نمایید:

1
2
3
4
5
6
7
if ($user->can('update', $post)) {
   //
}
if ($user->cannot('update', $post)) {
   //
}
<button></button>

داخل قالب های Blade

دستور @can از توابع Blade نیز برای مجوزدهی به action ها از policy هایی که به عنوان آرگومان به آن ارسال شده استفاده می نماید:

1
2
3
4
@can('update', $post)
   <!-- The Current User Can Update The Post -->
@endcan
<button></button>

از طریق تابع کمکی Policy

با بهره گیری از تابع کمکی سراسری policy می توان کلاس Policy را برای یک نمونه کلاس معین بازیابی نمود. برای مثال می توانیم یک نمونه از Postرا به helper (تابع کمکی)Policy ارسال کرده و در خروجی یک نمونه از کلاسPostPolicy مربوطه دریافت نماییم:

1
2
3
4
if (policy($post)->update($user, $post)) {
   //
}
<button></button>

مجوزدهی در کنترلر (Controller Authorization)

به طور پیش فرض کلاس پایه یApp\Http\Controllers\Controller (که با نصب لاراول در اختیار برنامه نویس قرار می گیرد) از مشخصه (trait)AuthorizesRequests برای مجوزدهی استفاده می کند. این trait متد authorize را ارائه می دهد که به وسیله ی آن می توان به سرعتaction خاصی را مجوز دهی نموده و همچنین چنانچه action مورد نظر مجوزدهی نشد، یک خطای HttpException صادر کند.
متد authorize از نظر signature (تعداد پارامترهای ورودی و نوع آن ها) با دیگر توابع مجوزدهی نظیر Gate::allows و $user->can() یکسان است. حال از متد authorize برای مجوزدهی سریع به یک درخواست برای بروز آوری یک Post استفاده می کنیم:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
 namespace App\Http\Controllers;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
      public function update($id)
   {
       $post = Post::findOrFail($id);
        $this->authorize('update', $post);
        // Update Post...
   }
}
<button></button>

چنانچه action مورد نظر مجوزدهی شد، کنترلر با روال عادی به اجرا ادامه می دهد. با این حال اگر متد authorize تشخیص دهد که action مورد نظر مجوزدهی نشده، یک خطای HttpException به صورت خودکار صادر می شود. به دنبال آن خطای نام برده یک پاسخ HTTP با کد وضعیت 403 Not Authorized تولید می کند.
همان طور که مشاهده می شود، متد authorize به شما امکان می دهد تا به راحتی و با نوشتن تنها یک خط کد action را مجوزدهی نموده یا در صورت عدم موفقیت مجوزدهی یک خطا صادر کنید.
AuthorizesRequests همچنین یک متد به نام authorizeForUser عرضه می کند که به وسیله ی آن می توان یک action را برای کاربری که در حال حاضر کاربر سنجش هویت شده (authenticated user) نیست، مجوز دهی نمود:

1
2
                    $this->authorizeForUser($user, 'update', $post);
<button></button>

تشخیص خودکار متدهای Policy

به کرات پیش می آید که متدهای یک policy با توابع موجود در کنترلر همخوانی داشته و مطابقت دارند. به عنوان مثال می توان به متد update در نمونه ی فوق اشاره کرد: متد موجود در کنترلر و policy هر دو update نام دارند.
به همین دلیل لاراول به شما این امکان را می دهد تا به راحتی آرگومان های instance را به متد authorize ارسال کنید. در پی آن ability ای که مجوزدهی می شود نیز با توجه به اسم تابع فراخواننده به صورت خودکار مشخص می گردد. در مثال حاضر از آنجایی متد authorize از تابع updateکلاس کنترلر صدا زده شده، تابع نام برده (update) در کلاس PostPolicy نیز فراخوانی می گردد:

1
2
3
4
5
6
7
public function update($id)
{
   $post = Post::findOrFail($id);
   $this->authorize($post);
   // Update Post...
}
<button></button>
1395/03/03 5981 1760
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...