使用者角色許可權控制包 Laravel-permission 使用說明

學冰發表於2018-04-12

此文章根據 laravel-permission官方說明檔案 翻譯

此軟體包允許你管理資料庫中的使用者許可權和角色。

安裝後,你可以做這樣的事情:

// 向使用者新增許可權
$user->givePermissionTo('edit articles');

// 通過角色新增許可權
$user->assignRole('writer');

$role->givePermissionTo('edit articles');

如果你使用多個守衛( guard ),則將會被覆蓋。每個分配給使用者的守衛,都有自己的一套許可權和角色。請閱讀使用自述檔案的 多重保護 部分。

由於所有許可權都將在 Laravel's gate 上進行註冊,因此你可以使用 Laravel 預設的 can 方法測試使用者是否具有許可權:

$user->can('edit articles');

Spatie 是比利時安特衛普的一家網頁設計機構。 您可以在我們的 網站 上找到我們所有開源專案的概述。

安裝

Laravel

該軟體包可用於Laravel 5.4 或更高版本。 如果您使用的是舊版本的 Laravel ,請檢視該軟體包的 v1 分支

您可以通過 composer 安裝軟體包:

composer require spatie/laravel-permission

在 Laravel 5.5 中,服務提供商將自動獲得註冊。 在舊版本的框架中,只需在 config/app.php 檔案中新增服務提供者即可:

'providers' => [
    // ...
    Spatie\Permission\PermissionServiceProvider::class,
];

你可以通過以下方式 釋出遷移

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"

如果你為你的 User 模型使用 UUID 或 GUID,你可以更新 create_permission_tables.php 的遷移,並用下面的程式碼替換為 $table->morphs('model')

$table->uuid('model_id');
$table->string('model_type');

遷移釋出後,你可以通過執行遷移來建立角色和許可權表:

php artisan migrate

你可以釋出配置檔案:

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"

釋出時 config/permission.php 配置檔案 包含:

return [

    'models' => [

        /*
         * 當使用這個包中的 “HasRoles” 特性時,我們需要知道應該
         * 使用哪個 Eloquent 模型來獲取您的許可權。
         * 當然,它通常只是“許可權(Permission)”模型,你也可以使用任何你喜歡的模型。
         * 
         * 您使用的許可權模型必須實現
         *  `Spatie\Permission\Contracts\Permission` 契約。
         */

        'permission' => Spatie\Permission\Models\Permission::class,

        /*
         * 當使用這個包中的 “HasRoles” 特性時,
         * 我們需要知道應該使用哪個 Eloquent 模型來檢索你的角色。
         * 當然,它通常只是 “角色(Role)” 模型,你也可以使用任何你喜歡的模型。
         *
         * 您使用的許可權模型必須實現
         * `Spatie\Permission\Contracts\Role` 契約。
         */

        'role' => Spatie\Permission\Models\Role::class,

    ],

    'table_names' => [

        /*
         * 當使用這個包中的 “HasRoles” 特性時,
         * 我們需要知道哪個表應該用來檢索你的“角色”。 
         * 我們選擇了一個基本的預設值,但您可以輕鬆將其更改為您喜歡的。
         */

        'roles' => 'roles',

        /*
         * 當使用這個包中的 “HasRoles” 特性時,
         * 我們需要知道哪個表應該用來檢索你的許可權。 
         * 我們選擇了一個基本的預設值,但您可以輕鬆將其更改為您喜歡的任何表。
         */

        'permissions' => 'permissions',

        /*
         * 
         * 當使用這個包中的 “HasRoles” 特徵時,
         * 我們需要知道應該使用哪個表來檢索你的“模型許可權”。 
         * 我們選擇了一個基本的預設值,但您可以輕鬆將其更改為您喜歡的任何表。
         * 
         */

        'model_has_permissions' => 'model_has_permissions',

        /*
         * 當使用這個包中的 “HasRoles” 特性時,
         * 我們需要知道哪個表應該用來檢索你的“模型角色”。 
         * 我們選擇了一個基本的預設值,但您可以輕鬆將其更改為您喜歡的任何表。
         */

        'model_has_roles' => 'model_has_roles',

        /*
         * 當使用這個包中的 “HasRoles” 特性時,
         * 我們需要知道應該使用哪個表來檢索您的“角色許可權”。 
         * 我們選擇了一個基本的預設值,但您可以輕鬆將其更改為您喜歡的任何表。
         */

        'role_has_permissions' => 'role_has_permissions',
    ],

    /*
     * 預設情況下,所有許可權將被快取24小時,
     * 除非更新許可或者更新角色來立即重新整理快取。
     */

    'cache_expiration_time' => 60 * 24,

    /*
     * 設定為 true 時,所需的許可權/角色名稱( permission/role)將新增到異常訊息中。
     * 在某些情況下,這可能被認為是資訊洩漏,
     * 所以為了獲得最佳安全性,預設設定為 false。
     */

    'display_permission_in_exception' => false,
];

Lumen

你可以通過 Composer 安裝軟體包:

composer require spatie/laravel-permission

複製必要的檔案:

cp vendor/spatie/laravel-permission/config/permission.php config/permission.php
cp vendor/spatie/laravel-permission/database/migrations/create_permission_tables.php.stub database/migrations/2018_01_01_000000_create_permission_tables.php

你還需要在 config/auth.php 建立另一個配置檔案。 在 Laravel 儲存庫中獲取它,或者執行以下命令:

curl -Ls https://raw.githubusercontent.com/laravel/lumen-framework/5.5/config/auth.php -o config/auth.php

現在,執行你的遷移:

php artisan migrate

然後,在 bootstrap/app.php 中註冊中介軟體:

$app->routeMiddleware([
    'auth'       => App\Http\Middleware\Authenticate::class,
    'permission' => Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role'       => Spatie\Permission\Middlewares\RoleMiddleware::class,
]);

以及配置和服務提供者:

$app->configure('permission');
$app->register(Spatie\Permission\PermissionServiceProvider::class);

用法

首先,將 Spatie\Permission\Traits\HasRoles 特徵新增到您的 User 模型中:

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;

    // ...
}
  • 注意,如果你需要在另一個模型繼承 Page 中使用 HasRoles 特性,你還需要新增 protected $guard_name = 'web'; 以及那個模型,否則你會得到一個錯誤
use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Traits\HasRoles;

class Page extends Model
{
   use HasRoles;

   protected $guard_name = 'web'; // 使用任何你想要的守衛

   // ...
}

此軟體包允許使用者與許可權和角色關聯。 每個角色都可以與多個許可權關聯。 “角色”和“許可權”是常規的 Eloquent 模型。 他們需要一個 name 並且可以像這樣建立:

use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

$role = Role::create(['name' => 'writer']);
$permission = Permission::create(['name' => 'edit articles']);

可以使用以下方法中的一種將許可權分配給角色:

$role->givePermissionTo($permission);
$permission->assignRole($role);

可以使用以下方法之一將多個許可權同步到一個角色:

$role->syncPermissions($permissions);
$permission->syncRoles($roles);

使用以下方法之一可以從角色中刪除許可權:

$role->revokePermissionTo($permission);
$permission->removeRole($role);

如果你使用多個警衛,那麼 guard_name 屬性也需要設定。 在自述檔案的使用 多重守衛 部分閱讀它。

// 獲取直接分配給使用者的所有許可權的列表
$permissions = $user->permissions;

// 獲取使用者通過角色繼承的所有許可權
$permissions = $user->getAllPermissions();

// 獲取所有已定義角色的集合
$roles = $user->getRoleNames(); // 返回一個集合

HasRoles 特徵(trait)還為您的模型新增了一個“角色”範圍,以便將查詢範圍限定為某些角色或許可權:

$users = User::role('writer')->get(); // 僅返回具有角色 'writer' 的使用者

role 範圍可以接受一個字串,一個 \Spatie\Permission\Models\Role 物件或一個 \Illuminate\Support\Collection 物件。

同樣的特徵(trait)也增加了一個範圍,只讓使用者擁有一定的許可權。

$users = User::permission('edit articles')->get(); // 僅返回具有  'edit articles' (繼承或直接)許可權的使用者

作用域可以接受一個字串,一個 \Spatie\Permission\Models\Permission 物件或 \Illuminate\Support\Collection 物件。

使用“直接(direct)”許可權(請參閱下面的內容以使用的角色和許可權)

可以授予任何使用者許可權:

$user->givePermissionTo('edit articles');

// 你也可以一次賦予多個許可權
$user->givePermissionTo('edit articles', 'delete articles');

// 你也可以傳遞一個陣列
$user->givePermissionTo(['edit articles', 'delete articles']);

可以撤銷使用者許可權:

$user->revokePermissionTo('edit articles');

或者一次性撤消並新增新的許可權:

$user->syncPermissions(['edit articles', 'delete articles']);

你可以測試使用者是否擁有許可權:

$user->hasPermissionTo('edit articles');

或者你可以傳遞一個代表許可權 ID 的整數

$user->hasPermissionTo('1');
$user->hasPermissionTo(Permission::find(1)->id);
$user->hasPermissionTo($somePermission->id);

...如果使用者有多個許可權:

$user->hasAnyPermission(['edit articles', 'publish articles', 'unpublish articles']);

你也可以傳遞整數以通過許可權 ID 進行查詢

$user->hasAnyPermission(['edit articles', 1, 5]);

已儲存的許可權將在預設警衛的 Illuminate\Auth\Access\Gate 類中註冊。 因此,你可以使用 Laravel 預設的 can 功能測試使用者是否擁有許可權:

$user->can('edit articles');

通過角色使用許可權

角色可以分配給任何使用者:

$user->assignRole('writer');

// 你也可以一次分配多個角色
$user->assignRole('writer', 'admin');
// 或者作為一個陣列
$user->assignRole(['writer', 'admin']);

角色可以從使用者中刪除:

$user->removeRole('writer');

角色也可以同步:

// 所有當前角色將從使用者中刪除,並替換為給定的陣列中的角色
$user->syncRoles(['writer', 'admin']);

你可以確定使用者是否具有某個角色:

$user->hasRole('writer');

你還可以確定使用者是否具有任何給定的角色列表:

$user->hasAnyRole(Role::all());

你還可以確定使用者是否具有所有給定的角色列表:

$user->hasAllRoles(Role::all());

assignRolehasRolehasAnyRolehasAllRolesremoveRole 方法可以接受字串、\Spatie\Permission\Models\Role 物件或 \Illuminate\Support\Collection 物件。

可以給角色一個許可:

$role->givePermissionTo('edit articles');

你可以確定角色是否具有某種許可權:

$role->hasPermissionTo('edit articles');

可以從角色撤銷許可權:

$role->revokePermissionTo('edit articles');

givePermissionTorevokePermissionTo 方法可以接受一個字串或 Spatie\Permission\Models\Permission 物件。

許可權是從角色自動繼承的。 另外,個人許可權也可以分配給使用者。 例如:

$role = Role::findByName('writer');
$role->givePermissionTo('edit articles');

$user->assignRole('writer');

$user->givePermissionTo('delete articles');

在上面的示例中,角色被授予編輯文章的許可權,並且該角色被分配給使用者。 現在,使用者可以編輯文章並刪除文章。 'delete articles' 的許可權是使用者的直接許可,因為它直接分配給他們。 當我們呼叫 $user->hasDirectPermission('delete articles') 時,它會返回 true ,而對 $user->hasDirectPermission('edit articles') 返回 false

如果你為應用程式中的角色和使用者構建許可權並希望限制或更改使用者角色的繼承許可權(即僅允許更改使用者的直接許可權),則此方法非常有用。

你可以列出所有這些許可權:

// 直接許可權
$user->getDirectPermissions() // Or $user->permissions;

// 從使用者角色繼承的許可權
$user->getPermissionsViaRoles();

// 適用於使用者的所有許可權(繼承和直接)
$user->getAllPermissions();

所有這些響應都是 Spatie\Permission\Models\Permission 物件的集合。

如果我們按照前面的例子,第一個響應將是一個具有 delete article 許可權的集合,第二個響應將是一個具有 edit article 許可權的集合,第三個將包含兩者。

使用 Blade 指令

此軟體包還新增了 Blade 指令來驗證當前登入的使用者是否具有全部或任何給定的角色列表。

或者你可以傳入 guard 作為第二個引數來執行檢查。

Blade 和角色

測試一個特定的角色:

@role('writer')
    I am a writer!
@else
    I am not a writer...
@endrole

同上

@hasrole('writer')
    I am a writer!
@else
    I am not a writer...
@endhasrole

測試列表中的任何角色:

@hasanyrole($collectionOfRoles)
    I have one or more of these roles!
@else
    I have none of these roles...
@endhasanyrole
// or
@hasanyrole('writer|admin')
    I am either a writer or an admin or both!
@else
    I have none of these roles...
@endhasanyrole

測試所有角色:

@hasallroles($collectionOfRoles)
    I have all of these roles!
@else
    I do not have all of these roles...
@endhasallroles
// or
@hasallroles('writer|admin')
    I am both a writer and an admin!
@else
    I do not have all of these roles...
@endhasallroles

Blade 和許可權

此軟體包不會新增任何特定於許可權的 Blade 指令。 因此,使用 Laravel 原生的 @can 指令來檢查使用者是否具有某種許可權。

@can('edit articles')
  //
@endcan

or

@if(auth()->user()->can('edit articles') && $some_other_condition)
  //
@endif

使用多個守衛( guard )

當使用預設的 Laravel 身份驗證配置時,所有上述方法都可以使用,不需要額外的配置。

但是,使用多個守衛時,他們將充當您的許可權和角色的名稱空間。 每個守衛都有自己的一套許可權和角色,並分配給他們的使用者模型。

對多個守衛使用許可權和角色

預設情況下,預設守衛( config('auth.defaults.guard') )將用作新許可權和角色的守衛。 在為特定守衛建立許可權和角色時,你必須在模型上指定他們的 guard_name

// 為管理員使用者建立 superadmin 角色
$role = Role::create(['guard_name' => 'admin', 'name' => 'superadmin']);

// 為屬於管理員的管理員使用者定義一個 `publish articles` 許可權 
$permission = Permission::create(['guard_name' => 'admin', 'name' => 'publish articles']);

// 為屬於 web 守衛的常規使用者定義一個 *不同的*   `publish articles` 許可權
$permission = Permission::create(['guard_name' => 'web', 'name' => 'publish articles']);

檢查使用者是否具有特定守衛的許可權:

$user->hasPermissionTo('publish articles', 'admin');

分配許可權和角色來保護使用者

您可以使用相同的方法為使用者分配許可權和角色,如上所述,通過 角色使用許可權。只要確保許可權或角色上的 guard_name 與使用者的守衛相匹配,否則會丟擲 GuardDoesNotMatch 異常。

使用 Blade 指令和多個守衛

你可以在 blade (在 blade 中使用)中的第二個引數傳入你想要它通過的守衛:

@role('super-admin', 'admin')
    I am a super-admin!
@else
    I am not a super-admin...
@endrole

使用中介軟體

此軟體包附帶 RoleMiddlewarePermissionMiddleware 中介軟體。 你可以將它們新增到你的 app/Http/Kernel.php 檔案中。

protected $routeMiddleware = [
    // ...
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
];

然後,你可以使用中介軟體規則保護你的路由:

Route::group(['middleware' => ['role:super-admin']], function () {
    //
});

Route::group(['middleware' => ['permission:publish articles']], function () {
    //
});

Route::group(['middleware' => ['role:super-admin','permission:publish articles']], function () {
    //
});

或者,您可以用 |(管道)字元分隔多個角色或許可權:

Route::group(['middleware' => ['role:super-admin|writer']], function () {
    //
});

Route::group(['middleware' => ['permission:publish articles|edit articles']], function () {
    //
});

通過在建構函式中設定所需的中介軟體,可以同樣保護您的控制器:

public function __construct()
{
    $this->middleware(['role:super-admin','permission:publish articles|edit articles']);
}

捕獲角色和許可權失敗

如果你想覆蓋預設的 403 響應,你可以使用你的應用程式異常處理器來捕獲 UnauthorizedException

public function render($request, Exception $exception)
{
    if ($exception instanceof \Spatie\Permission\Exceptions\UnauthorizedException) {
        // 你的程式碼在這 ...
    }

    return parent::render($request, $exception);
}

使用 artisan 命令

你可以使用 artisan 命令從控制檯建立角色或許可權。

php artisan permission:create-role writer
php artisan permission:create-permission "edit articles"

在為特定守衛建立許可權和角色時,可以將守衛名稱指定為第二個引數:

php artisan permission:create-role writer web
php artisan permission:create-permission "edit articles" web

單元測試

在你的應用程式的測試中,如果你沒有將角色和許可權作為你的測試 setUp() 的一部分進行播種,那麼你可能遇到一個先有雞還是先有蛋的情況,角色和許可權沒有在 gate 註冊(因為在 gate註冊完成之後,測試會自動建立它們)。 解決這個問題很簡單:在你的測試中,只需新增一個 setUp() 指令來重新註冊許可權,如下所示:

    public function setUp()
    {
        // 首先,包括所有正常的 setUp 操作
        parent::setUp();

        // 現在,重新註冊所有角色和許可權
        $this->app->make(\Spatie\Permission\PermissionRegistrar::class)->registerPermissions();
    }

資料庫填充

有關資料庫填充的兩個注意事項

  1. 最好在填充之前重新整理 spatie.permission.cache ,以避免快取衝突錯誤。 可以通過 Artisan 命令(稍後參見故障排除:快取部分)或直接在填充類中完成(參見下面的示例)。

  2. 以下是一個示例填充類,它清除快取,建立許可權併為角色分配許可權:

    use Illuminate\Database\Seeder;
    use Spatie\Permission\Models\Role;
    use Spatie\Permission\Models\Permission;
    
    class RolesAndPermissionsSeeder extends Seeder
    {
       public function run()
       {
           // 重置角色和許可權快取
           app()['cache']->forget('spatie.permission.cache');
    
           // 建立許可權
           Permission::create(['name' => 'edit articles']);
           Permission::create(['name' => 'delete articles']);
           Permission::create(['name' => 'publish articles']);
           Permission::create(['name' => 'unpublish articles']);
    
           // 建立角色並分配建立的許可權
    
           $role = Role::create(['name' => 'writer']);
           $role->givePermissionTo('edit articles');
    
           $role = Role::create(['name' => 'moderator']);
           $role->givePermissionTo(['publish articles', 'unpublish articles']);
    
           $role = Role::create(['name' => 'super-admin']);
           $role->givePermissionTo(Permission::all());
       }
    }

擴充套件

如果你需要擴充套件現有的 RolePermission 模型,請注意:

  • 你的 Role 模型需要擴充套件 Spatie\Permission\Models\Role 模型
  • 你的 Permission 模型需要擴充套件 Spatie\Permission\Models\Permission 模型

如果你需要替換現有的 RolePermission 模型,則需要記住以下事項:

  • 你的 Role 模型需要實現 Spatie\Permission\Contracts\Role 契約
  • 你的 Permission 模型需要實現 Spatie\Permission\Contracts\Permission 契約

在這兩種情況下,無論是擴充套件還是替換,你都需要在配置中指定新模型。 為此,你必須在使用此命令釋出配置後更新配置檔案中的 models.rolemodels.permission 值:

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"

快取

快取角色和許可權資料以加速效能。

當你使用提供的方法來操作角色和許可權時,快取會自動為您重置:

$user->assignRole('writer');
$user->removeRole('writer');
$user->syncRoles(params);
$role->givePermissionTo('edit articles');
$role->revokePermissionTo('edit articles');
$role->syncPermissions(params);
$permission->assignRole('writer');
$permission->removeRole('writer');
$permission->syncRoles(params);

但是,如果直接在資料庫中操作“許可權/角色”資料而不是呼叫提供的方法,則除非手動重置快取,否則不會在應用程式中看到反映的更改。

手動快取重置

要手動重置此軟體包的快取,請執行:

php artisan cache:forget spatie.permission.cache

快取識別符號

提示:如果你正在利用快取服務,如 redismemcached ,並且伺服器上還有其他站點正在執行,則可能會遇到快取衝突。 在 /config/cache.php 中將自己的快取 字首 設定為每個應用程式的唯一內容是謹慎的。 這將防止其他應用程式意外地使用或更改快取的資料。

需要一個 UI ?

該軟體包沒有隨附任何螢幕,你應該自行構建。 要開始檢視 Caleb Oki 撰寫的 大量教程

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章