liutauras / 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
This package is not auto-updated.
Last update: 2024-10-03 03:16:16 UTC
README
描述
Laravel自带访问控制列表(ACL),用于处理用户权限。然而,如果您需要构建一个基于角色和权限的灵活访问系统,并支持继承,请考虑它们的触发逻辑。此外,将所有这些保存到数据库中,基本功能将不足以满足需求。当前的laravel包是一个简单而强大的工具,它允许通过简单的应用程序集成开发常规RBAC和最复杂系统。
这个包支持什么?
- 多用户模型
- 角色和权限的无限制继承(带无限循环检查)
- 用户的多角色和权限
- 在通过守卫或其他方式分离它们的情况下,auth项(角色、权限等)的可扩展性
- 设置权限触发的逻辑(见 进阶使用)
- 通过名称、ID或类实例检查角色和权限
- 通过特性、门、中间件、Blade指令进行权限检查
- 启用和禁用缓存的能力
- Artisan-commands用于处理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应用程序中添加您需要的任何新类型的auth-items。
创建、删除、继承角色和权限
要添加新的角色或权限
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.
将角色和权限附加到用户
通过特性(trait)从模型侧启用授权模型。 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)使用
使用门面(facade)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字段(ID Folder View)设为1。
当用户尝试通过$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中更改表名。
