kojit2009/laravel-roles-permissions

用于处理Laravel中角色和权限的简单但强大的包

1.0.6 2019-03-14 14:50 UTC

This package is auto-updated.

Last update: 2024-09-22 15:41:28 UTC


README

描述

Laravel自带访问控制列表(ACL),可用于处理用户权限。然而,如果您需要构建基于角色和权限的灵活访问系统,并支持继承,请考虑其触发逻辑。此外,将所有这些保存在数据库中,基本功能是不够的。当前的Laravel包是一个简单的强大工具,它允许通过简单的应用程序集成开发标准RBAC和最复杂的系统。

此包支持什么?

  • 多个用户模型
  • 角色和权限的无级继承(带有无限循环检查)
  • 用户可以有多个角色和权限
  • 在按守卫或其他方式分离时,可以扩展认证项目(角色、权限等)的可扩展性
  • 设置权限触发逻辑(见高级使用
  • 通过名称、ID或类实例检查角色和权限
  • 通过特性、门控、中间件、Blade指令进行权限检查
  • 启用和禁用缓存的权限
  • Artisan命令用于处理RBAC

安装

您可以通过composer安装此包

composer require centeron/laravel-roles-permissions

您可以通过以下命令发布配置文件

php artisan vendor:publish --provider="Centeron\Permissions\ServiceProvider" --tag="config"

执行命令后,您可以在config/permissions.php文件中更改数据库表名。您还可以更改缓存设置。

使用以下命令发布迁移

php artisan vendor:publish --provider="Centeron\Permissions\ServiceProvider" --tag="migrations"

执行迁移

php artisan migrate

使用

首先将特性 Centeron\Permissions\Traits\HasAuthItems 添加到您的模型 User

use Illuminate\Foundation\Auth\User as Authenticatable;
use Centeron\Permissions\Traits\HasAuthItems;

class User extends Authenticatable
{
    use HasAuthItems;

    // ...
}

现在 User 可以添加角色和权限、检查访问权限等。每个用户都可以分配多个角色和权限。权限可以分配给角色、模型和其他父权限。对于角色也有同样的机会。实际上,角色和权限之间没有真正的区别。它仅仅是根据逻辑类型对授权项目(auth-items)的一种正式划分。请随意在您的Laravel应用程序中添加所需的新类型授权项目。

创建、删除、继承角色和权限

要添加新的角色或权限

use Centeron\Permissions\Models\AuthItem;

$role = AuthItem::createRole(['name' => 'admin']);
$role = AuthItem::create(['name' => 'admin', 'type' => AuthItem::TYPE_ROLE]); // alternative to the previous code

$permission = AuthItem::createPermission(['name' => 'admin']);
$permission = AuthItem::create(['name' => 'admin', 'type' => AuthItem::TYPE_PERMISSION]); // alternative to the previous code

AuthItem 是一个扩展的Eloquent模型。因此,您可以使用所需的任何Eloquent命令,包括删除

use Centeron\Permissions\Models\AuthItem;

AuthItem::where('name', 'admin')->delete();

使用 addChildsaddParents 添加父项和子项,组织所需的任何深度的层次结构

$role->addChilds($permission1, $permission2, $subRole);
$permission2->addChilds($permission2_1);
$permission2_1_1->addParents($permission2_1);

同样,使用 removeChildsremoveParents 删除这些关系(不删除AuthItem本身实体)。

在上面的例子中,变量是AuthItem的实例。然而,您可以使用角色和权限的名称或甚至ID作为参数。方法通过参数的类型(int/string/class)识别如何处理这些参数。例如,以下代码也可以正确运行

$permission = AuthItem::where('name', 'Create post')->first();

$role->addChilds($permission, 'Update post', 4);
$role->removeParents($permission, 4);

您可以使用hasAny函数(有给定的任何项目)和hasAll函数(有给定的所有项目)检查项目是否有其他项目的权限(换句话说,是否在任何一代中是子项)。当然,如果只给定一个参数,这些方法相等。

$hasAny = $role->hasAny('Update post', 4); // true, because permission 'Update post' presents
$hasAll = $role->hasAll('Update post', 4); // false, because permission with ID=4 absents. 

将角色和权限分配给用户

授权模型可以通过特质从模型端进行授权。 Centeron\Permissions\Traits\HasAuthItems

$user->attachAuthItems('admin', $otherRoleModel, 'Create post', 4); 

从角色/权限端通过将模型作为参数传递给它们

$otherRoleModel->attach($user)

可以使用detachAuthItems从用户中移除权限(角色)

$user->detachAuthItems('Create post'); 

通过特性使用

您可以确定用户是否有角色和权限

$user->hasAnyAuthItems('View post', 'Edit post'); // true, if it has any of a given list
$user->hasAllAuthItems('View post', 'Edit post'); // true, if it has all of a given list

但有时用户只能编辑自己的帖子,或者只有在他们对该帖子有适当的评级时才能编辑。在这种情况下,我们必须使用其他函数

$user->canAnyAuthItems(['View post', 'Edit post'], [1]); // true, possible edit or view a post with ID = 1
$user->hasAllAuthItems(['View post', 'Edit post'], [1]); // true, possible edit and view post with ID = 1

有关条件访问的更多内容,请参阅第高级用法章节

通过门控使用

使用外观Gate检查权限的标准方法也有效。您仍然可以使用Gatecheckallowdenies方法

      if (Gate::denies('Edit post', $post)) {}
      if (Gate::allows('Delete comment', [$post, $comment])) {}
      if (Gate::check('admin')) {}      

您不需要使用define方法。所有定义都存储在数据库中。考虑了权限继承。

在控制器中,您也可以使用辅助函数

    $this->authorize('Edit post', $post);

通过中间件使用

您可以使用带有内置中间件can的中间件规则来保护您的路由

Route::match(['get', 'post'], 'post/view', 'PostController@view')->middleware('can:View post');

通过Blade指令使用

view文件中,使用Blade指令很方便

@authHasAny('Edit post', 'View post')
    // Showing info if a user can edit or view post 
@authEnd

@authHasAll('Edit post', 'View post')
    // Showing info if a user can edit and view post 
@authElse
    // Otherwise
@authEnd

对于条件权限,以下指令应该使用

@authCanAny(['Edit post', 'View post'], [1])
    // Showing info if a user can edit or view post with ID=1 
@authEnd

@authCanAll(['Edit post', 'View post'], [1])
    // Showing info if a user can edit and view post with ID=1
@authElse
    // Otherwise
@authEnd

有关条件访问的更多内容,请参阅第高级用法章节

Artisan命令

当前包提供了与RBAC一起工作的必要控制台命令的完整列表

php artisan centeron:auth-item-create // Create a new auth item (role or description)
php artisan centeron:auth-items-remove // Remove auth items (roles and permissions)
php artisan centeron:auth-item-inherit // Add child items to chosen auth item
php artisan centeron:auth-item-disinherit // Remove childs from an auth item
php artisan centeron:auth-items-attach // Attach auth items to the model
php artisan centeron:auth-items-detach // Deattach auth items from the model

权限的高级使用

绝大多数RBAC使用不需要任何触发逻辑。我们用字符串标识符标记区域,然后检查用户是否有权限访问该特定区域。有时这还不够。如果用户只需要编辑他们自己的帖子或属于特定类别的帖子怎么办?随着时间的推移,可能还会添加新的类别。在这种情况下,重新编码并不总是必要的。

首先,我们应该能够向权限提供触发逻辑,其次,保留可能需要这些规则的相关数据。

为此,Centeron\Permissions\Models\AuthItem有两个属性(表中的列)

  • rule - 存储负责触发逻辑的类的名称
  • data - 以序列化形式存储可能需要与当前对象一起工作的数据。

rule是一个实现Centeron\Permissions\Contracts的类,它只有一个方法handle。如果此方法返回true,则权限/角色AuthItem将启用,如果返回false,则忽略当前权限。

作为示例,此包提供了3个规则

权限authItem仅在周末可用

namespace Centeron\Permissions\Rules;

use Centeron\Permissions\Contracts\Rule;

class OnWeekdDays implements Rule
{
    public function handle($authItem, $model, $params = []): bool
    {
        return date('N') <= 5;
    }
}

用户只能访问他们自己创建的对象

namespace Centeron\Permissions\Rules;

use Centeron\Permissions\Contracts\Rule;

class OnWeekdDays implements Rule
{
    public function handle($authItem, $model, $params = []): bool
    {
       $entityId = $params[0] ?? null;
       return $model->id === $entityId;
    }
}

在这种情况下,我们必须提供有关对象(ID)的信息作为参数。

用户只能访问数据库中已列出的某些类别

namespace Centeron\Permissions\Rules;

use Centeron\Permissions\Contracts\Rule;

class OnWeekdDays implements Rule
{
    public function handle($authItem, $model, $params = []): bool
    {
        $myCategories = unserialize($authItem['data']);
        return array_intersect($params, $myCategories) ? true : false;
    }
}

在这种情况下,我们必须提供有关类别的信息作为参数。此外,必须将具有条件访问的类别的信息保存在表的data字段中。

显然,每个用户都可以与一个特定的类别列表相关联。在这种情况下,您不应该为每个用户创建具有特定data和相同rule的新的AuthItem。相反,您可以在外键表中保存数据,并将此数据作为参数提供,包括其他信息。

我们对条件权限执行访问检查,不是通过hasAnyAuthItemshasAllAuthItems方法,而是通过canAnyAuthItemscanAnyAuthItems方法,这些方法只需要两个参数。第一个参数是角色/权限或角色/权限的数组,第二个参数是变量的数组

$user->canAnyAuthItems(['View post', 'Edit post'], [1]); // true, possible edit or view a post with ID = 1
$user->hasAllAuthItems(['View post', 'Edit post'], [1]); // true, possible edit and view a post with ID = 1

您还可以通过特质方法canAuthItems确定传递的列表中附加到用户的权限

Blade指令按照这些规则工作。这同样适用于authorize方法和Gate方法。

根据参数分割用户之间的访问

AuthItem 中的 base_auth_id 字段可以是空的。它可以用来与其他 AuthItem 连接,这是一个基础项。方法 canAuthItemscanAnyAuthItemscanAllAuthItems 不仅使用给定的权限(及其继承),还考虑 base_auth_id 权限。

让我们看看用户在文件管理器中只需要访问自己的文件夹的情况。负责访问的权限是 AuthItem,ID 为 1,名称为 Folder View。我们只需要为每个用户创建新的权限 AuthItem,其中文件夹名称在 data 字段中,规则处理程序在 rule 字段中。在 base_auth_id 字段中设置 1(ID Folder View)。

当用户尝试通过 $user->canAnyAuthItems(['Folder View'], ['folder_name']); 访问 folder_name 文件夹时,不仅会检查 Folder View,还会检查其他权限,以及具有 base_auth_id = 1 的权限,将 folder_nameAuthItemdata 应用 rule 进行匹配。

缓存

尽管数据库请求相对较多(从 3 到 5,具体情况而定),但由于这些请求对数据库的简单性(从索引列中选择),访问检查仍然很快。缓存获取和保存每个表的表数据。

通过提供的 RBAC 方法更改表将重置缓存。您也可以手动重置缓存。

php artisan cache:forget centeron.permissions

通过编辑 config/permissions.php 来启用/禁用缓存,设置缓存有效期,以及其标识符。

注意!在应用有数百或数千个角色和权限以及它们与用户的关系的情况下,由于缓存了所有表的信息,使用的内存有时会急剧增加。但处理时间大致相同。即使数据库表中记录数以千计,与禁用缓存一起工作也不需要大量的内存,因为请求仅与当前检查所需的必要相关数据相关。

数据库结构

为了实现所有工作,Laravel RBAC 只需要 3 个数据库表。

  • auth_items - 角色
  • auth_item_childs - 角色和权限的继承
  • auth_assignments - 角色和权限的分配以及模型

在运行迁移之前,可以在 config/permissions.php 中更改表名。

Структура таблиц