kojit2009 / 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
中更改表名。