用户角色权限控制包 Laravel-permission 使用说明

此文章根据 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 协议》,转载必须注明作者和本文链接
本帖由系统于 6年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 17

怎样获取指定角色的权限呢?一直没找到方法

5年前 评论

楼主,上面的$user 哪里定义的啊?

5年前 评论

@chris_zqw 如果是给当前用户的话,就是
$id = Auth::user()->id;
$user = User::findOrFail($id);

5年前 评论
考拉

@wxiangbo 在角色模型中已经定义方法了permissions(),他是一个关联关系,多对多
所以,你可以这样获取

$permissions = $role->permissions;  //动态属性获取
$permissions = $role->permissions()->get();   //关联关系获取
5年前 评论
zhengwhizz

使用多重守卫时,cache 不对,$user->hasPermissionTo('create role','api'); 第一次正常,但是当要使用其它守卫时,$user->hasPermissionTo('create role','api2'); 依旧返回之前的值,甚至给它一个不存在的守卫名称,依然返回旧值,经过调试,发现第二次取的缓存Key信息里只有create role,并没有守卫相关的信息,所以一直取到的是第一次的缓存值,是我用的不对?还是有什么其它解决办法?

5年前 评论

model_type 目前看都是App\User 这个字段有详细的说明和作用吗?

5年前 评论

楼主 @学冰 怎么实现多个数据库用户权限管理呢 ,如 有A 、B两个数据库 A和B的表结构都是一样的 用户表结构都是一样的,计划走不通的登录接口走不同的库,这个包似乎要走一个默认的数据库链接。这个怎么解决呀!

4年前 评论
raybon 4年前

权限控制多一个选择,

使用基于Casbin的Laravel权限控制:https://github.com/php-casbin/laravel-auth...

4年前 评论
liziyu 2年前

上手使用感觉

laravel-permission

  1. 数据表设计,更加复杂一些
  2. 数据表进行设计的时候,并不是每张表都有id作为PRI,导致部分CRUD工具无法识别主键
  3. guard 会默认从配置中获取web,这就要求Model类是要有guard_name这个属性的。或者可以每次调用givePermissionTo之前,单独指定属性。

bouncer

  1. 数据表结构设计更简单,数据结构设计更加灵活,不仅仅是给用户设置权限,可以给任何Model设置(当然laravel-permission也可以)
  2. permissions表,用来存储了权限和ablities表的映射关系,但是这张表里面的entity_type既可以是roles、又可以是Model类,entity_id代表在entity_type指向的表中的主键
  3. ablities才是权限表

总结

从刚刚进行适配的感觉来看,bouncer 更适合自由度更高的programer

推荐

这是一款可以根据MySQL的表结构生成CRUD后台的工具,在配合使用laravel-permission的时候,遇到了很多问题,就如刚刚提到的主键问题。配合bouncer运行完美,可以修改后作为权限系统的UI。

https://github.com/sunshinev/laravel-gii
4年前 评论
myCrane 4年前

遇到一个很神奇的问题 可以用权限查到拥有该权限的用户,但是根据用户判断是否有某个权限,查询该用户的所有权限的时候都不可以 我的spatie/laravel-permission是3.18版本的。有没有老哥遇到过这个问题的

3年前 评论
ThreeNewBee 2年前
雄赳赳 1年前

建议看一下官网文档,我觉得更系统一些!~

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!