mr_dreek / laravel-roles-permissions
用于处理Laravel中角色和权限的简单而强大的包
Requires
- php: ^8.0
- laravel/framework: ^10.0|^11.0
Requires (Dev)
- roave/security-advisories: dev-master
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应用程序中添加您所需的新类型的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.
将角色和权限附加到用户
模型授权可以通过使用特性来实现,从模型侧开始。 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
有2个属性(表中的列)。
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
。我们所需做的只是为每个用户创建新的具有文件夹名称在data
字段和规则处理程序在rule
字段中的AuthItem
权限。在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
中更改表名。