spatie/Laravel-permission Laravel 应用中的角色和权限控制

姬欣怡
2023-12-01

将用户与角色进行关联

这个包允许你在数据库中管理用户的权限和角色。

当你安装了扩展包之后你就可以这样做:

// 给用户添加一个权限
$user->givePermissionTo('edit articles');

// 通过角色添加权限。
$user->assignRole('writer');

// 给角色添加一个权限
$role->givePermissionTo('edit articles');

如果你给单个用户添加了多个守卫 (guard),扩展包也可以处理的很好,每一个分配给用户的守卫都有它自己的权限和角色,阅读 using multiple guards 章节可以看见更多的信息。

因为所有的权限将注册在 Laravel's gate 上,所以你可以调用 Laravel 默认的 'can' 方法来测试用户是否有权限:

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

Spatie 是一个位于 Antwerp, Belgium 的 web 设计机构。你可以在我们的官网找到所有的开源项目。

安装
Laravel
这个包可以在 Laravel 5.4 或更高版本中使用,如果你使用的是旧版本的 Laravel,可以切换到 这个包的 v1 分支 去使用。

你可以通过 composer 去安装这个包:

composer require spatie/laravel-permission

在 Laravel 5.5 中 service provider 会自动注册,旧版本的 Laravel 中你需要像以下这样自行添加到 config/app.php 中:

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

你可以使用以下命令发布 migration 

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

如果你在 User 模型中使用 UUIDs 或 GUIDs 你可以修改 create_permission_tables.php migration 并替换 $table->morphs('model'):

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

migration 发布后,您可以通过运行以下命令来创建角色和权限表:

php artisan migrate

你可以运行以下命令生成配置文件:

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

当发布了配置文件后,就可以看到 config/permission.php 中包括:

return [

    'models' => [

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * Eloquent model should be used to retrieve your permissions. Of course, it
         * is often just the "Permission" model but you may use whatever you like.
         *
         * The model you want to use as a Permission model needs to implement the
         * `Spatie\Permission\Contracts\Permission` contract.
         */

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

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * Eloquent model should be used to retrieve your roles. Of course, it
         * is often just the "Role" model but you may use whatever you like.
         *
         * The model you want to use as a Role model needs to implement the
         * `Spatie\Permission\Contracts\Role` contract.
         */

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

    ],

    'table_names' => [

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your roles. We have chosen a basic
         * default value but you may easily change it to any table you like.
         */

        'roles' => 'roles',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your permissions. We have chosen a basic
         * default value but you may easily change it to any table you like.
         */

        'permissions' => 'permissions',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your models permissions. We have chosen a
         * basic default value but you may easily change it to any table you like.
         */

        'model_has_permissions' => 'model_has_permissions',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your models roles. We have chosen a
         * basic default value but you may easily change it to any table you like.
         */

        'model_has_roles' => 'model_has_roles',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your roles permissions. We have chosen a
         * basic default value but you may easily change it to any table you like.
         */

        'role_has_permissions' => 'role_has_permissions',
    ],

    /*
     * By default all permissions will be cached for 24 hours unless a permission or
     * role is updated. Then the cache will be flushed immediately.
     */

    'cache_expiration_time' => 60 * 24,

    /*
     * When set to true, the required permission/role names are added to the exception
     * message. This could be considered an information leak in some contexts, so
     * the default setting is false here for optimum safety.
     */

    'display_permission_in_exception' => false,
];

使用

首先,添加 Spatie\Permission\Traits\HasRoles trait 到 User 模型:

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

class User extends Authenticatable
{
    use HasRoles;

    // ...
}
  • 请注意,如果你需要在另一个模型,例如 Page 中添加 HasRoles trait 你还需要添加 protected $guard_name = 'web'; 到这个模型中,否则会报错。
  • use Illuminate\Database\Eloquent\Model;
    use Spatie\Permission\Traits\HasRoles;
    
    class Page extends Model
    {
       use HasRoles;
    
       protected $guard_name = 'web'; // or whatever guard you want to use
    
       // ...
    }

    这个包允许用户与权限和角色相关联。每个角色都与多个权限相关联。
    Role 和 Permission 都是 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 必须要设置,在 使用多守卫 段落中有提及到。

    HasRoles trait 具有 Eloquent 模型关系功能,可以通过关系去直接访问或用作基本的查询:

    // 获取直接分配给用户的所有的权限
    $permissions = $user->permissions;
    
    // 返回所有用户通过赋予角色所继承的权限
    $permissions = $user->getAllPermissions();
    
    // 获取所有已定义的角色的集合
    $roles = $user->getRoleNames(); // 返回一个集合

    HasRoles trait 在你的模型上还增加了 role scope 能让你检索特定角色或者特定权限的用户:

  • $users = User::role('writer')->get(); // 返回角色是 'writer' 的用户

    role scope 接收一个字符串,\Spatie\Permission\Models\Role 对象或者 \Illuminate\Support\Collection 对象。

    这个 trait 还增加了 scope 让你只能获取到具有某个权限的用户。

    $users = User::permission('edit articles')->get(); // 只返回有 'edit articles' 权限的用户 (继承角色得来的或者是直接分配的)

    role scope 接收一个字符串,\Spatie\Permission\Models\Permission 对象或者 \Illuminate\Support\Collection 对象。

    使用 "直接" 权限 (可以参考下面 roles 和 permissions 的使用)
    分配权限给任何一个用户:

    $user->givePermissionTo('edit articles');
    
    // You can also give multiple permission at once
    $user->givePermissionTo('edit articles', 'delete articles');
    
    // You may also pass an array
    $user->givePermissionTo(['edit articles', 'delete articles']);

    撤销用户的某个权限:

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

在一次操作中撤销或者新增权限:

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

你可以判断某个用户是否具有这个权限:

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

判断用户是否具有多个权限:

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

保存了权限后它将会注册在 Illuminate\Auth\Access\Gate 类的默认守卫中。所以你可以使用 Laravel 的默认 can 方法来判断用户是否有某个权限:

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

通过角色使用权限

角色可以被分配给任意用户:

$user->assignRole('writer');

// You can also assign multiple roles at once
$user->assignRole('writer', 'admin');
// or as an array
$user->assignRole(['writer', 'admin']);

角色可以从一个用户身上移除:

$user->removeRole('writer');

角色也可以被同步:

// All current roles will be removed from the user and replaced by the array given
$user->syncRoles(['writer', 'admin']);

你可以判断一个用户是否包含某个角色:

$user->hasRole('writer');

你也可以判断一个用户是否包含给定角色列表中的一个:

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

assignRole, hasRole, hasAnyRole, hasAllRoles 和 removeRole 这些函数可以接受一个字符串,一个 \Spatie\Permission\Models\Role 对象 或者 一个 \Illuminate\Support\Collection 对象作为参数。

权限可以被分配给一个角色:

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

你可以判断一个角色是否包含某个权限:

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

权限也可以从一个角色身上移除:

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

givePermissionTo 和 revokePermissionTo 函数可以接受一个字符串或者一个 Spatie\Permission\Models\Permission 对象作为参数。

权限是从角色中自动继承的.
另外,个人权限也可以分配给用户.
例如:

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

$user->assignRole('writer');

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

在上面的例子中,角色被赋予了编辑文章的权限,并且该角色被分配给了用户。
现在,用户可以编辑、删除文章。“删除” 权限是直接分配给用户的直接权限。
当我们调用 $user->hasDirectPermission('delete articles') 它会返回 true,
而 false 对应的是 $user->hasDirectPermission('edit articles').

如果为应用程序中的角色和用户设置权限,并希望限制或者更改用户角色的继承权限(仅允许更改用户的直接权限),则这个方法就会非常有用。

你可以列出这些权限:

// Direct permissions
$user->getDirectPermissions() // Or $user->permissions;

// Permissions inherited from the user's roles
$user->getPermissionsViaRoles();

// All permissions which apply on the user (inherited and direct)
$user->getAllPermissions();

所有的响应都是 Spatie\Permission\Models\Permission 这个对象的集合.

如果我们按照之前的例子,第一个响应将是一个具有 delete article 权限的集合,第二个响应会是一个具有 edit article 权限的集合,而第三个响应将会包含之前二者的集合。

使用 Blade 语法
这个包还增加了 Blade 语法来验证当前登录的用户是否具有全部或某个给定的角色。

你可以通过传入第二个参数 guard 来进行检索。

Blade 语法 Role
测试一个特定的角色:

@role('writer')
    我是一个 writer!
@else
   我不是一个 writer...
@endrole

相当于

@hasrole('writer')
    我是一个 writer!
@else
   我不是一个 writer...
@endhasrole

测试传入的列表中的任一角色:

@hasanyrole($collectionOfRoles)
    我有一个或多个这里的权限
@else
    这些权限我都没有
@endhasanyrole
// or
@hasanyrole('writer|admin')
    我是一个 writer 或者 admin
@else
    我既不是 writer 也不是 admin
@endhasanyrole

测试是否具有所有角色

@hasallroles($collectionOfRoles)
    这些角色我都是
@else
    这些角色我都不是
@endhasallroles
// or
@hasallroles('writer|admin')
    我既是 writer 也是 admin
@else
    我不是 writer 也不是 admin
@endhasallroles

Blade 模板 与 Permissions 权限
本扩展没有提供权限相关的特殊的 Blade directives,而是使用 Laravel 自带的 @can directive 来校验用户权限。举个栗子:

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

再举个栗子:

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

使用多个 guards
使用 Laravel 默认的 auth 配置时,以上例子的所有的方法都可以正常使用,不需要做额外的配置。
当有多个 guards 时,校验权限和角色时有点像命名空间。意味着每个 guard 都有与之用户模型相关的的角色和权限。

通过多重守卫来使用权限和角色
新增的权限和角色会默认使用系统默认的 guard (config('auth.defaults.guard')) 。新增权限和角色的时候,可以通过 model 的属性 guard_name 来指定 guard:

// Create a superadmin role for the admin users
$role = Role::create(['guard_name' => 'admin', 'name' => 'superadmin']);

// Define a `publish articles` permission for the admin users belonging to the admin guard
$permission = Permission::create(['guard_name' => 'admin', 'name' => 'publish articles']);

// Define a *different* `publish articles` permission for the regular users belonging to the web guard
$permission = Permission::create(['guard_name' => 'web', 'name' => 'publish articles']);

校验用户是否有指定 guard 的权限:

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

用户授权和角色赋予
你可以使用上文所述的方法 using permissions via roles 给用户授权和赋予角色。仅需要确保权限或者角色的 guard_name 与用户的 guard 是一样的,否则将会抛出一个 GuardDoesNotMatch 异常

blade directives 中使用 multiple guards
所有的 blade directivesusing blade directives 对于多个 guards 都是同样适用,只需要将 guard_name 作为第二个参数即可。举个栗子:

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

使用中间件

这个包包含 RoleMiddleware 和 PermissionMiddleware 俩个中间件。 你可以把他们添加到 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 () {
    //
});

另外,你可以通过 | (pipe) 字符把多个角色或者权限区分开:

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) {
        // Code here ...
    }

    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


原文作者:Summer
转自链接:https://learnku.com/laravel/t/8018/extension-recommendation-role-and-authority-control-in-spatielaravel-permission-laravel-applications#842f03
 

 类似资料: