centeron / laravel-roles-permissions
用于处理Laravel中角色和权限的简单而强大的包
Requires
- php: >=7.0
- laravel/framework: ~5.4.0|~5.5.0|~5.6.0|~5.7.0|~5.8.0
Requires (Dev)
- phpunit/phpunit: ^5.7|6.2|^7.0
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();
使用 addChilds
和 addParents
添加父项和子项,组织任何深度的层次结构
$role->addChilds($permission1, $permission2, $subRole); $permission2->addChilds($permission2_1); $permission2_1_1->addParents($permission2_1);
类似地,使用 removeChilds
和 removeParents
删除这些关系(而不会删除实体 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
检查权限的标准方式也有效。您仍然可以使用 Gate
的 check
、allow
和 denies
方法
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
。相反,您可以在外键表中保存数据,并将这些数据作为参数与其他信息一起提供给类。
我们不是通过 hasAnyAuthItems
和 hasAllAuthItems
方法,而是通过 canAnyAuthItems
和 canAnyAuthItems
方法执行条件权限的准入检查,这两个方法只需要两个参数。第一个参数是角色/权限或角色/权限数组,第二个是变量数组
$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
连接,这些是基础项。方法canAuthItems
、canAnyAuthItems
、canAllAuthItems
不仅使用给定的权限(及其继承),还考虑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_name
与AuthItem
的data
匹配,并应用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
中更改表名。