使用者授權,策略的使用

一句話兒發表於2022-03-07

限制當前使用者操作。如:當前使用者只能操作自己的資料。

使用者僅能編輯,刪除,自己釋出的文章。

文章簡化表結構articles如下:

articles 
    id - integer 
    title - string 
    content - text 
    user_id - integer

策略是圍繞特定模型或資源組織授權邏輯的類。

生成策略

生成文章操作限制策略類:

你可以使用 make:policy Artisan 命令生成策略。生成的策略將放置在 app/Policies 目錄中。如果應用程式中不存在此目錄,Laravel 將自動建立。
生成空的授權策略類:

php artisan make:policy ArticlePolicy

生成一個包含與檢視、建立、更新和刪除資源相關的示例策略方法的類,可以在執行命令時提供一個 --model 選項:

php artisan make:policy ArticlePolicy --model=Article

手動註冊策略發現

註冊策略是告知 Laravel 在授權針對給定模型型別的操作時使用哪個策略。

Laravel 應用程式中包含的 App\Providers\AuthServiceProvider 包含一個 policies 屬性,它將 Eloquent 模型對映到其相應的策略。 註冊策略將指示 Laravel 在授權針對給定 Eloquent 模型的操作時使用哪個策略:

<?php

namespace App\Providers;

use App\Models\Article;
use App\Policies\ArticlePolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 應用程式的策略對映。
     */
    protected $policies = [
        Article::class => ArticlePolicy::class, // 指定Article模型使用ArticlePolicy::class策略類
    ];

    /**
     * 註冊任何應用程式身份驗證/授權服務。
     */
    public function boot()
    {
        $this->registerPolicies();
    }
}

策略自動發現

只要模型和策略遵循標準的 Laravel 命名約定,Laravel 就可以自動發現策略,而不是手動註冊模型策略。遵守約定,指定模型註冊指定模型策略。

具體約定示例:模型可以放置在 app/Models目錄中,而策略可以放置在 app/Policies目錄中。在這種情況下,Laravel 將檢查app/Policies中的策略。此外,策略名稱必須與模型名稱匹配並具有Policy字尾。 因此,User 模型將對應於 UserPolicy 策略類。

自定義策略發現

可以使用 Gate::guessPolicyNamesUsing 方法註冊自定義策略發現回撥。通常,應該從應用程式的 AuthServiceProviderboot 方法呼叫此方法:

<?php

namespace App\Providers;

use App\Models\Article;
use App\Policies\ArticlePolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 應用程式的策略對映。
     *
     * @var array
     */
    protected $policies = [
        Article::class => ArticlePolicy::class, // 指定Article模型使用ArticlePolicy::class策略類
    ];

    /**
     * 註冊任何應用程式身份驗證/授權服務。
     */
    public function boot()
    {
        $this->registerPolicies();
        //自定義策略發現
        Gate::guessPolicyNamesUsing(function ($modelClass) {
            //指定該模型使用的策略類。如: 'App\Models\Article' => 'App\Policies\ArticlePolicy',
            return 'App\Policies\\'.class_basename($modelClass).'Policy';
        });
    }
}

編寫策略

<?php

namespace App\Policies;

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

class ArticlePolicy
{

    /**
     * 使用者是否可以檢視文章
     * @param \App\Models\User  $user
     * @return mixed
     */
    public function view(User $user)
    {
      //限制
    }

    /**
     * 確定使用者是否可以刪除給定的文章
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Article  $article
     * @return bool
     */
    public function delete(User $user, Article $article)
    {
        return $user->id === $article->user_id;
    }
}

策略響應

自定義響應資訊,可以從你的策略方法返回一個 Illuminate\Auth\Access\Response 例項

use App\Models\Article;
use App\Models\User;
use Illuminate\Auth\Access\Response;

/**
 * Determine if the given article can be delete by the user.
 *
 * @param  \App\Models\User  $user
 * @param  \App\Models\Article  $$article
 * @return \Illuminate\Auth\Access\Response;
 */
public function delete(User $user, Article $article)
{
    return $user->id === $article->user_id
                ? Response::allow()
                : Response::deny('You do not own this article.');
}

策略過濾器

對於某些使用者,您可能希望給他授權給定策略中的所有操作。為了實現這一點,你可以在策略上定義一個 before 方法。

before 方法將在策略上的所有方法之前執行,這樣就使您有機會在實際呼叫預期的策略方法之前就已經授權了操作。該功能常用於授權應用程式管理員來執行任何操作:

use App\Models\User;

/**
 * 執行預先授權檢查
 *
 * @param  \App\Models\User  $user
 * @param  string  $ability
 * @return mixed
 */
public function before(User $user, $ability)
{
    if ($user->isAdministrator()) {
        return true;
    }
}

如果你想拒絕特定型別使用者的所有授權檢查,那麼你可以從 before 方法返回 false 。如果返回 null ,則授權檢查將透過策略方法進行。

推薦策略類繼承自定義基類,便於統一做許可權限制

<?php

namespace App\Policies;

use Illuminate\Auth\Access\HandlesAuthorization;

class Policy
{
    use HandlesAuthorization;

    public function __construct()
    {
        //
    }

    /**
     * 執行預先授權檢查
     *
     * @param  \App\Models\User  $user
     * @param  string  $ability
     * @return mixed
     */
    public function before($user, $ability)
    {
        if ($user->isAdmin()) {
            return true;
        }
    }
}

注意:如果策略類中不包含名稱與被檢查能力的名稱相匹配的方法,則不會呼叫策略類的 before 方法。

使用策略授權操作

透過控制器輔助函式

authorize 方法接收您希望授權的操作名稱和相關模型,如果該操作未被授權,該方法將丟擲 Illuminate\Auth\Access\AuthorizationException 異常,Laravel 的異常處理程式將自動將該異常轉換為一個帶有 403 狀態碼的 HTTP 響應:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{
    /**
     * 刪除指定的文章
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Article  $article
     * @return mixed
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function destory(Request $request, Article $article)
    {
        $this->authorize('delete', $article);
    }
}

不需要指定模型的操作

一些策略方法 如 create 不需要模型例項,在這些情況下,你應該給 authorize 方法傳遞一個類名,該類名將用來確定在授權操作時使用哪個策略:

use App\Models\Article;
use Illuminate\Http\Request;

/**
 * Create a new article.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return mixed
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function create(Request $request)
{
    $this->authorize('view', Article::class);
}

策略類提供額外的上下文

在使用策略授權操作時,可以將陣列作為第二個引數傳遞給授權函式和輔助函式。

陣列中的第一個元素用於確定應該呼叫哪個策略,其餘的陣列元素作為引數傳遞給策略方法,並可在作出授權決策時用於額外的上下文中。

例如,考慮下面的 ArticlePolicy 方法定義,它包含一個額外的 $category 引數:

/**
 * 確認使用者是否可以更新給定的文章
 *
 * @param  \App\Models\User  $user
 * @param  \App\Models\Article  $article
 * @param  int  $category
 * @return mixed
 */
public function update(User $user, Article $article, int $category)
{
    //做相應限制
}

/**
 * 確認使用者是否可以刪除給定的文章
 *
 * @param  \App\Models\User  $user
 * @param  array $ids
 * @return mixed
 */
public function delete(User $user,  $ids)
{
    //做相應限制
}

當嘗試確認已驗證過的使用者是否可以更新給定的文章時,我們可以像這樣呼叫此策略方法:

/**
 * 更新給定的文章
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \App\Models\Article  $article
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function update(Request $request, Article $article)
{
    $this->authorize('update', [$article, $request->category]);
}

/**
 * 刪除給定的文章
 *
 * @param  \Illuminate\Http\Request  $request
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function destory(Request $request)
{
    $this->authorize('delete', [Article::class, $request->ids]);
}

Laravel 中所有異常都是由 App\Exceptions\Handler 類處理。

use \Illuminate\Auth\Access\AuthorizationException;

/**
* Register the exception handling callbacks for the application.
*/
public function register()
{
    $this->renderable(function (AuthorizationException $e, $request) {
        return response()->json([
            'message' => '暫無許可權'
        ], 403);
    });
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
寫的不好,就當是整理下思緒吧。

相關文章