[扩展包] JosephSilber/bouncer 的使用

在社区中没找到 JosephSilber/bouncer 的翻译。自己瞎翻译了一下。才疏学浅,用了整整一下午才翻译完。可能有很多翻译的不好的地方,还望各位大佬指正。里面有部分内容原文是在不同地方的,我把他们放一起了,方便查看。如:缓存。
原文地址:https://github.com/JosephSilber/bouncer#al...

Bouncer 是一个优雅的,与框架无关的扩展包,它可以使用 Eloquent 模型管理任何应用程序的角色和权限。

它使用富有表现力和流畅的语法,对它你可以呼之则来,挥之则去。

安装

通过 composer 来安装:

$ composer require silber/bouncer v1.0.0-rc.1

安装完成后,可以通过以下步骤来使用( laravel 5.5 以上版本服务提供者和别名会自动注册,可以跳过1,2步):

  1. provider 数组中添加一个新的元素:
    Silber\Bouncer\BouncerServiceProvider::class,

  2. aliases 数组中添加新元素:
    'Bouncer'=>Silber\Bouncer\BouncerFacade::class,

  3. 向用户模型中添加 Bouncer 的 Trait

use Silber\Bouncer\Database\HasRolesAndAbilities;

class User extends Model
{
    use HasRolesAndAbilities;
}
  1. 执行 Bouncer 的数据迁移,通过以下命令,将迁移发布到你的 migrations 目录中。
    php artisan vendor:publish --tag="bouncer.migrations"

  2. 最后执行数据迁移
    php artisan migrate

Facade(门面)

使用 Bouncer 门面时,你需要在文件顶部的命名空间处添加一行:
use Bouncer;
如果想了解更多 Laravel Facade 的相关信息,可以去 Laravel 文档 了解;

启用缓存

默认情况,Bouncer 的查询请求将会被缓存,为了获得更好的性能,你可能会使用到 跨请求 缓存。

Bouncer::cache();

当你启用了 跨请求 缓存时,你需要在每次更改角色/能力时都刷新缓存;

Bouncer :: refresh();

完全刷新所有用户的缓存时,将使用 缓存标记 (如果你的驱动支持的话,参阅 Laravel 文档 确认你的驱动是否支持缓存标记),并非所有驱动都支持缓存标记,对于不支持缓存标记的用户,调用 fresh 方法可能会比较慢,具体取决于用户数量;

当然,你也可以为特定用户刷新缓存:

Bouncer::refreshFor($user);

禁用缓存

当你想禁用缓存时,可以使用:

Bouncer::dontCache();

当你在单元测试中,想针对刚被授权的用户/能力进行断言时非常有用;

用法

向用户添加角色和权限将变得非常容易。你无需事先创建一个角色或者权限。只需要传递角色/权限的名称,如果名称不存在,Bouncer 将会自动创建;
创建一个角色并使他可以在网站中禁用用户:

Bouncer::allow('admin')->to('ban-users');

在幕后,Bouncer 将会帮你创建一个 Role 模型和 Ability 模型。

如果你想为角色/能力添加更多属性,例如人类可读的标题,你可以使用 BOuncer 下面的 Role 和 Ability 方法创建它们。

$admin = Bouncer::role()->firstOrCreate([
    'name' => 'admin',
    'title' => 'Administrator',
]);

$ban = Bouncer::ability()->firstOrCreate([
    'name' => 'ban-users',
    'title' => 'Ban users',
]);

Bouncer::allow($admin)->to($ban);

为用户分配角色

为用户赋予一个 admin 的角色:

Bouncer::assign('admin')->to($user);

也可以用用户模型调用 assign 方法赋予:

$user->assign('admin');

为用户分配某个权限

有时候,你需要绕开角色,直接给用户赋予某个权限:

Bouncer::allow($user)->to('ban-users');

同样,你还可以直接使用用户模型调用 allow 方法:

$user->allow('ban-users');

限制模型能力

当你想限制某个特定模型类型的权限时,可以将模型名字作为第二个参数传入:

Bouncer::allow($user)->to('edit', Post::class);

如果要限制特定模型实例的权限,则将实例化模型作为第二参数传入:

Bouncer::allow($user)->to('edit', $post);

允许用户或者角色 “拥有” 一个模型

可以使用 toOwn 方法允许用户管理它们自己的模型:

Bouncer::allow($user)->toOwn(Post::class);

现在,当在 gate 中检查用户是否可以在给定的帖子中执行某个动作时,帖子的 user_id 将会和已登录用户的 id 进行比较。如果匹配,gate 将会允许这个动作。

用户将会被赋予“拥有的”模型所有权限。你可以调用 to 方法来限制他的权限。

Bouncer::allow($user)->toOwn(Post::class)->to('view');

// 或者传递一系列的权限:
Bouncer::allow($user)->toOwn(Post::class)->to(['view', 'update']);

你也可以在应用中允许用户拥有所有类型的模型:

Bouncer::allow($user)->toOwnEverything();

// 或限制指定权限的所有权
Bouncer::allow($user)->toOwnEverything()->to('view');

从用户中撤回角色

Bouncer 也可以从用户中撤回某个预设的角色:

Bouncer::retract('admin')->from($user);

也可以使用用户模型的 retract 方法撤回:

$user->retract('admin');

移除权限

BOuncer 同样可以移除先前授予的权限:

Bouncer::disallow($user)->to('ban-users');

也可以用用户模型的 dissallow 方法撤回:

$user->disallow('ban-users');

注意:如果用户拥有一个允许他们 ban-users 的角色,那么,该用户还是会拥有这个权限,你只有把用户的该角色删除,或者将角色中的 ban-users 权限删除,该用户才不会有 ban-users 权限。

删除角色中的某个已有的权限:

Bouncer::disallow('admin')->to('ban-users');

删除特定模性类型的权限,请将模型名称作为第二个参数传入:

Bouncer::disallow($user)->to('delete', Post::class);

如果用户拥有某个特定的 $post 实例的 delete 权限,以上代码并不会将它的权限删除,你需要通过传入 $post 实例作为第二个参数来删除这个权限,如下:

删除特定模型实例的权限,将实例作为第二参数传入:

Bouncer::disallow($user)->to('delete', $post);

disallow 方法仅仅移除了事先赋予这个用户/角色的权限,如果你想移除一些已被赋予的更为概括的权限的子集,可以使用 forbid 方法。

禁用一项权限

Bouncer 也允许你使用 forbid 方法禁用一项已被赋予的权利,以实现更细致的权限控制。同时,你也许会希望授权用户/角色涵盖了各种操作的权限,随后限制这些操作的一笑部分;

这儿有一个例子:

  • 你也许允许用户浏览所有文档,但是有个特殊的高度机密文档不想被他看见:
Bouncer::allow($user)->to('view', Document::class);

Bouncer::forbid($user)->to('view', $classifiedDocument);
  • 你也许允许 superadmin 在你的 app 里面做任何事情,包括增加/移除用户。随后你需要一个 admin 角色可以执行除了用户管理的任何事情;
Bouncer::allow('superadmin')->everything();

Bouncer::allow('admin')->everything();
Bouncer::forbid('admin')->toManage(User::class);
  • 你也许希望偶尔禁用用户,取消他们被授予的所有权限。然而,通常移除他们所有的角色和权限将意味着重新再授权时,我们要弄清他们的原有权限是什么。

使用禁用能力意味着他们还拥有原来的角色和权限,但是不被允许做任何事情。我们可以通过创建一个特殊的角色--bannde 来实现这一目的。

Bouncer::forbid('banned')->everything();

然后,将想要禁用的用户授予 banned 角色:

Bouncer::assign('banned')->to($user);

移除禁令时,只需要将该角色移除即可:

Bouncer::retract('banned')->from($user);

如你所见,BOouncer的禁用功能将会让你能精细地控制你的 app 中的权限。

检测用户角色

总的来说,你不需要直接检查用户角色。你最好是允许角色拥有某些权限,然后检查这些权限。如果你的需求是粗略的,那么你可以建立一个广泛的权限。比如:检查是否拥有 access-dashboard 权限远比检查是否为 admin 或者 editor 角色要好。当你确实需要检查角色时,以下是检查角色的功能:

检查用户是否有特定的角色:

Bouncer::is($user)->a('moderator');

如果你检查的角色时元音开头,你需要使用 an 别名方法:

Bouncer::is($user)->an('admin');

相反的,你也可以检查用户是或否不具备某个特殊的角色:

Bouncer::is($user)->notA('moderator');

Bouncer::is($user)->notAn('admin');

你还可以检查用户是否拥有众多角色中的一个:

Bouncer::is($user)->a('moderator', 'editor');

你还可以检查用户是否拥有所有给定的角色:

Bouncer::is($user)->all('editor', 'moderator');

你还可以检查用户是否在给定的角色中一个都不具备:

Bouncer::is($user)->notAn('editor', 'moderator');

当然,以上方法都可以在用户实例中直接使用:

$user->isAn('admin');
$user->isA('subscriber');

$user->isNotAn('admin');
$user->isNotA('subscriber');

$user->isAll('editor', 'moderator');

获取用户所有角色

你可以直接从用户模型获取他的所有角色:

$roles = $user->getRoles();

获取用户所有权限

可以直接使用用户模型获取他的所有权限:

$abilities = $user->getAbilities();

这个会返回一个用户权限的集合,包括所有通过角色赋予这个用户的权限。

授权用户

授权用户直接在 Laravel Gate 中或者用户模型($user->can($ability))中处理。
为了更加方便,Bouncer 类提供了以下的直通方法:

Bouncer::can($ability);
Bouncer::cannot($ability);
Bouncer::authorize($ability);

这些方法直接调用 Gate 类。

Blade 指令

Bouncer 不会直接添加它自己的 blade 指令。由于 BOuncer 直接使用 Laravel 的Gate ,你可以直接使用它的 [[[[[[@can](https://learnku.com/users/12729)](https://learnku.com/users/12729)](https://learnku.com/users/12729)](https://learnku.com/users/12729)](https://learnku.com/users/12729)](https://learnku.com/users/12729) 来检查用户的权限;

@if ($user->isAn('admin'))
    //
@endif

多租户技术

Bouncer 完全支持多用户 app,在同一个 app 中为所有用户无缝地集成 Bouncer 的角色和权限。

scope 中间件

首先,将 scope 中间件 发布到你的 app 中。

php artisan vendor:publish --tag="bouncer.middleware"

执行完命令,scope 中间件会被发布到 app/Http/Middleware/ScopeBouncer.php 之中,这个中间件是你告诉 Bouncer 当前请求属于哪个租户的地方。

例如,假设你的用户全都都拥有 account_id 属性,那么你的中间件会是这样:

public function handle($request, Closure $next)
{
    $tenantId = $request->user()->account_id;

    Bouncer::scope()->to($tenantId);

    return $next($request);
}

你也可以按需修改这个中间件,比如从子区块获取租户信息。

有了中间件,你需要在 HTTP Kernel 中注册它。

protected $middlewareGroups = [
    'web' => [
        // 原有的中间件请保存, 然后添加以下这行:
        \App\Http\Middleware\ScopeBouncer::class,
    ]
];

所有的 Bouncer 查询现在将会被指定给特定租户。

自定义 Bouncer scope

根据你的 app 的设置,你也许不想要将所有的查询都作用于当前租户。
例如,你的租户也许有一套相同的固定的角色/权限配置,并且,仅仅允许用户控制为哪些用户分配哪些角色,为哪些角色分配哪些权限。为了实现这个功能,你可以告诉
Bouncer scope 仅限制 Bouncer 的模型之间的关系,而不是模型本身。

Bouncer::scope()->to($tenantId)->onlyRelations();

此外,你的 app 甚至可能不允许用户控制给定角色具有哪些权限。此时,你可以告诉 Bouncer scope 从 scope 中排除角色能力,以便这些关系在所有租户中保持全局统一。

Bouncer::scope()->to($tenantId)->onlyRelations()->dontScopeRoleAbilities();

如果您的需求比上面的概述更专业的话,你可以创建自己的 Scope 来实现你的业务逻辑。

use Silber\Bouncer\Contracts\Scope;

class MyScope implements Scope
{
    // 你的 app 所需的自定义的逻辑
}

然后在服务提供者中注册你的自定义 scope:

Bouncer::scope(new MyScope);

Bouncer 将会在它执行的各个地方调用 Scope 中的方法。你可以根据你的特殊需求自由的处理。

配置

Bouncer 附带合理的默认值,因此大多数时候你不需要进行任何配置。对于更细粒度的控制,可以通过调用Bouncer类上的各种配置方法来自定义Bouncer 。

如果你只须有使用其中的一到两项配置选项,你可以将它们粘贴到你的 APPServiceProvider 的 boot 方法中。如果配置需要更多了,你就需要在 app/Providers 目录中创建一个分离的 BouncerServiceProvider 类(记得在 provider 配置数组中进行注册)。

数据表

要更改 Bouncer 使用的数据表名称,你需要传递一个联合数组给 tables 方法。
键名是 Bouncer 的默认表名,值是你要使用的表名。你只需要传递你想修改的表名,而不需要传递所有的表名。

Bouncer::tables([
    'abilities' => 'my_abilities',
    'permissions' => 'granted_abilities',
]);

Bouncer 发布的迁移使用此配置中的表名,因此请确保在实际运行迁移文件之前这些表存在。

自定义模型

你可以轻松的扩展 Bouncer 的内置 Role 和 Ability 模型:

use Silber\Bouncer\Database\Ability;

class MyAbility extends Ability
{
    // custom code
}
use Silber\Bouncer\Database\Role;

class MyRole extends Role
{
    // custom code
}

或者,你可以使用 Bouncer 的 IsAbility 和 IsRole 这两个 traits 而不实际扩展任何Bouncer的模型:

use Illuminate\Database\Eloquent\Model;
use Silber\Bouncer\Database\Concerns\IsAbility;

class MyAbility extends Model
{
    use IsAbility;

    // custom code
}
use Illuminate\Database\Eloquent\Model;
use Silber\Bouncer\Database\Concerns\IsRole;

class MyRole extends Model
{
    use IsRole;

    // custom code
}

如果你使用这个 traits 方法,请确保设置了正确的 $table 表名和 $fillable 字段。

不管使用哪种方法,下一步,你应该做的就是告诉 Bouncer 你要使用自定义的模型。

Bouncer::useAbilityModel(MyAbility::class);
Bouncer::useRoleModel(MyRole::class);

用户模型

默认情况下,Bouncer 会自动使用默认的用户防护用户模型。
(这里翻译的不是太明白,英语水平有限。请大佬指点:By default, Bouncer automatically uses the user model of the default auth guard.)
如果你将 Bouncer 与非默认防护一起使用,并且它使用的不是同一个用户模型,你需要告诉 Bouncer 你想使用的用户模型;

Bouncer::useUserModel(\App\Admin::class);

所有权

在 Bouncer 中,所有权的概念用于允许用户对他们“拥有”的模型执行操作。
默认情况下,Bouncer 会将模型的 user_id 字段与当前用户的主键进行对比。如果有需要,你可以自行指定字段。

Bouncer::ownedVia('userId');

如果不同模型使用不同字段的所有权,你可以分别注册他们:

Bouncer::ownedVia(Post::class, 'created_by');
Bouncer::ownedVia(Order::class, 'entered_by');

为了获得更好的控制,您可以使用自定义逻辑传递闭包:

Bouncer::ownedVia(Game::class, function ($game, $user) {
    return $game->team_id == $user->team_id;
});

FAQ

......

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 4年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 4

这个tables怎么修改,修改这个tables方法好像没有用

5年前 评论

@afoolishman

Bouncer::tables([
    'abilities' => 'my_abilities',
    'permissions' => 'granted_abilities',
]);

@afoolishman

5年前 评论

@Larwas 谢谢大佬,这段代码应该放在什么地方,我尝试了放在bouncer类的构造函数中,创建角色和权限正常,验证的时候还是显示不正常

5年前 评论

请问有实例可以看看嘛

5年前 评论

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