ligadaweb / defender
Laravel 的角色与权限
Requires
- php: >=7.2.0 || ^8.0
- laravel/framework: ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0
- phpspec/phpspec: dev-main
Requires (Dev)
- fakerphp/faker: ^1.0
- friendsofphp/php-cs-fixer: ~2.11 || ^3.51
- orchestra/database: ^4.0 || ^5.0 || ^6.0 || ^7.0
- orchestra/testbench: ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0
- phpunit/phpunit: ^8.0 || ^9.0 || ^10.0 || ^11.0
This package is auto-updated.
Last update: 2024-09-03 19:51:06 UTC
README
Defender 是 Laravel 5 / 6 / 7 / 8 / 9(单认证)的访问控制列表(ACL)解决方案。(不兼容多认证)
考虑到安全和可用性,此项目旨在为您提供一种安全的方式来控制应用程序访问,同时不失编码的乐趣。
当前构建状态
统计数据
欢迎贡献
Defender 正在寻找维护者和贡献者。
安装
1. 依赖
使用 composer,执行以下命令以自动更新您的 composer.json
,使用相应的包版本
composer require artesaos/defender
或手动更新您的 composer.json
文件
{ "require": { "artesaos/defender": "~0.10.0" } }
2. 提供者
如果您使用 Laravel >= 5.5,则可以跳过此部分,因为我们的包支持自动发现。
您需要更新应用程序配置以注册包,以便它可以由 Laravel 加载。只需更新您的 config/app.php
文件,在 'providers'
部分的末尾添加以下代码即可
// file START ommited 'providers' => [ // other providers ommited \Artesaos\Defender\Providers\DefenderServiceProvider::class, ], // file END ommited
3. 用户类
在您的用户类上,添加特质 Artesaos\Defender\Traits\HasDefender
以启用权限和角色的创建
<?php namespace App; use Illuminate\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Artesaos\Defender\Traits\HasDefender; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; class User extends Model implements AuthenticatableContract, CanResetPasswordContract { use Authenticatable, CanResetPassword, HasDefender; ...
如果您使用 laravel 5.2+,则有一些细微差别
<?php namespace App; use Artesaos\Defender\Traits\HasDefender; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasDefender; ...
4. 发布配置文件和迁移
要发布默认配置文件和数据库迁移,请执行以下命令
php artisan vendor:publish
执行迁移,以便在您的数据库上创建表
php artisan migrate
您还可以仅发布配置文件或迁移
php artisan vendor:publish --tag=config
或者
php artisan vendor:publish --tag=migrations
如果您已经发布了 defender 文件,但出于某种原因想要覆盖先前发布的文件,请添加 --force
标志。
5. Facade(可选)
为了使用 Defender
facade,您需要在 config/app.php
文件中注册它,您可以按照以下方式操作
// config.php file // file START ommited 'aliases' => [ // other Facades ommited 'Defender' => \Artesaos\Defender\Facades\Defender::class, ], // file END ommited
6. Defender 中间件(可选)
如果您必须通过 Defender 控制访问,Defender 提供中间件来保护您的路由。如果您必须通过 Laravel 路由控制访问,Defender 为一些简单任务内置了一些中间件。要使用它们,只需将它们放在您的 app/Http/Kernel.php
文件中。
protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, // Access control using permissions 'needsPermission' => \Artesaos\Defender\Middlewares\NeedsPermissionMiddleware::class, // Simpler access control, uses only the groups 'needsRole' => \Artesaos\Defender\Middlewares\NeedsRoleMiddleware::class ];
下面将介绍如何使用中间件。
6.1 - 创建自己的中间件
如果内置中间件不符合您的需求,您可以使用 Defender 的 API 来控制访问。
用法
Defender 只处理访问控制。身份验证仍然由 Laravel 的 Auth
执行。
注意:如果您使用不同的用户模型或已更改命名空间,请更新 defender 配置文件中的 user_model 键
创建角色和权限
使用命令
您可以使用这些命令为您的应用程序创建角色和权限。
php artisan defender:make:role admin # creates the role admin php artisan defender:make:role admin --user=1 # creates the role admin and attaches this role to the user where id=1 php artisan defender:make:permission users.index "List all the users" # creates the permission php artisan defender:make:permission users.create "Create user" --user=1 # creates the permission and attaches it to user where id=1 php artisan defender:make:permission users.destroy "Delete user" --role=admin # creates the permission and attaches it to the role admin
使用生成器或 artisan tinker
您还可以使用 Defender 的 API。您可以创建一个 Laravel Seeder 或使用 php artisan tinker
。
use App\User; $roleAdmin = Defender::createRole('admin'); // The first parameter is the permission name // The second is the "friendly" version of the name. (usually for you to show it in your application). $permission = Defender::createPermission('user.create', 'Create Users'); // You can assign permission directly to a user. $user = User::find(1); $user->attachPermission($permission); // or you can add the user to a group and that group has the power to rule create users. $roleAdmin->attachPermission($permission); // Now this user is in the Administrators group. $user->attachRole($roleAdmin);
使用中间件
要保护您的路由,您可以使用内置中间件。
Defender 需要 Laravel 的 Auth,因此,在使用您打算使用的 Defender 中间件之前,请使用
auth
中间件。
检查权限:needsPermissionMiddleware
Route::get('foo', ['middleware' => ['auth', 'needsPermission'], 'shield' => 'user.create', function() { return 'Yes I can!'; }]);
如果您使用 Laravel 5.1+,则可以使用 Middleware 参数。
Route::get('foo', ['middleware' => ['auth', 'needsPermission:user.index'], function() { return 'Yes I can!'; }]);
使用此语法,您还可以在控制器中使用中间件。
$this->middleware('needsPermission:user.index');
您可以将要检查的权限数组传递给。
Route::get('foo', ['middleware' => ['auth', 'needsPermission'], 'shield' => ['user.index', 'user.create'], function() { return 'Yes I can!'; }]);
使用中间件参数时,使用 |
来分隔多个权限。
Route::get('foo', ['middleware' => ['auth', 'needsPermission:user.index|user.create'], function() { return 'Yes I can!'; }]);
或者在控制器中
$this->middleware('needsPermission:user.index|user.create');
当您传递一个权限数组时,只有当用户拥有所有权限时,路由才会触发。但是,如果您希望当用户至少拥有一个权限时允许访问路由,只需添加'any' => true
。
Route::get('foo', ['middleware' => ['auth', 'needsPermission'], 'shield' => ['user.index', 'user.create'], 'any' => true, function() { return 'Yes I can!'; }]);
或者,使用中间件参数,将其作为第二个参数传递
Route::get('foo', ['middleware' => ['auth', 'needsPermission:user.index|user.create,true'], function() { return 'Yes I can!'; }]);
或者在控制器中
$this->middleware('needsPermission:user.index|user.create,true');
检查角色:needsRoleMiddleware
这与前面的中间件类似,但只检查角色,这意味着它不检查权限。
Route::get('foo', ['middleware' => ['auth', 'needsRole'], 'is' => 'admin', function() { return 'Yes I am!'; }]);
如果您使用的是Laravel 5.1,则可以使用中间件参数。
Route::get('foo', ['middleware' => ['auth', 'needsRole:admin'], function() { return 'Yes I am!'; }]);
使用此语法,您还可以在控制器中使用中间件。
$this->middleware('needsRole:admin');
您可以将要检查的权限数组传递给。
Route::get('foo', ['middleware' => ['auth', 'needsRole'], 'shield' => ['admin', 'member'], function() { return 'Yes I am!'; }]);
当使用中间件参数时,使用|
分隔多个角色。
Route::get('foo', ['middleware' => ['auth', 'needsRole:admin|editor'], function() { return 'Yes I am!'; }]);
或者在控制器中
$this->middleware('needsRole:admin|editor');
当您传递一个权限数组时,只有当用户拥有所有权限时,路由才会触发。但是,如果您希望当用户至少拥有一个权限时允许访问路由,只需添加'any' => true
。
Route::get('foo', ['middleware' => ['auth', 'needsRole'], 'is' => ['admin', 'member'], 'any' => true, function() { return 'Yes I am!'; }]);
或者,使用中间件参数,将其作为第二个参数传递
Route::get('foo', ['middleware' => ['auth', 'needsRole:admin|editor,true'], function() { return 'Yes I am!'; }]);
或者在控制器中
$this->middleware('needsRole:admin|editor,true');
在视图中使用
Laravel的Defender扩展,用于使用Defender。
@shield
@shield('user.index')
shows your protected stuff
@endshield
@shield('user.index')
shows your protected stuff
@else
shows the data for those who doesn't have the user.index permission
@endshield
您还可以使用通配符(*)
@shield('user.*')
shows your protected stuff
@else
shows the data for those who doesn't have the any permission with 'user' prefix
@endshield
@is
@is('admin')
Shows data for the logged user and that belongs to the admin role
@endis
@is('admin')
Shows data for the logged user and that belongs to the admin role
@else
shows the data for those who doesn't have the admin permission
@endis
@is(['role1', 'role2'])
Shows data for the logged user and that belongs to the admin role
@else
shows the data for those who doesn't have the admin permission
@endis
使用JavaScript助手
此库提供了在客户端与用户权限交互时的助手。
echo Defender::javascript()->render(); // or echo app('defender')->javascript()->render(); // or echo app('defender.javascript')->render();
此助手注入一段包含当前用户所有权限和角色的JavaScript代码。
使用门面
使用Defender的门面,您可以在应用的任何部分访问API并使用它。
Defender::hasPermission($permission)
:
检查登录用户是否有$permission
。
Defender::canDo($permission)
:
检查登录用户是否有$permission
。如果角色superuser
返回true
Defender::roleHasPermission($permission)
:
检查登录用户是否有$permission
,只检查角色权限。
Defender::hasRole($roleName)
:
检查登录用户是否属于角色$roleName
。
Defender::roleExists($roleName)
:
检查角色$roleName
是否存在于数据库中。
Defender::permissionExists($permissionName)
:
检查权限$permissionName
是否存在于数据库中。
Defender::findRole($roleName)
:
通过名称$roleName
在数据库中查找角色。
Defender::findRoleById($roleId)
:
通过角色ID roleId
在数据库中查找角色。
Defender::findPermission($permissionName)
:
通过名称$permissionName
在数据库中查找权限。
Defender::findPermissionById($permissionId)
:
通过ID $permissionId
在数据库中查找权限。
Defender::createRole($roleName)
:
在数据库中创建一个新的角色。
Defender::createPermission($permissionName)
:
在数据库中创建一个新的权限。
Defender::is($roleName)
:
检查当前用户是否属于该角色。
Defender::javascript()->render()
:
返回一个包含当前用户所有角色和权限列表的JavaScript脚本。变量名可以修改。
使用特质
要添加Defender的功能,您需要在User模型(通常是App\User
)中添加HasDefender
特质。
<?php namespace App; // Declaration of other omitted namespaces use Artesaos\Defender\Traits\HasDefender; class User extends Model implements AuthenticatableContract, CanResetPasswordContract { use Authenticatable, CanResetPassword, HasDefender; // Rest of the class }
除了配置关系外,此特质还会向您的对象App\User
添加以下方法
public function hasPermission($permission)
:
此方法检查登录用户是否有$permission
权限
在Defender中,有两种权限:用户权限
和角色权限
。默认情况下,用户继承的权限是其所属角色的权限。但是,当设置用户权限时,它将优先于角色权限。
public function foo(Authenticable $user) { if ($user->hasPermission('user.create')); }
public function roleHasPermission($permission)
:
此方法与上一个方法类似,唯一的区别是不考虑用户权限,然而,只使用用户所属的角色权限来检查访问。
public function foo(Authenticable $user) { if ($user->roleHasPermission('user.create'); }
public function attachRole($role)
:
将用户附加到角色$role
。变量$role
可能是一个类型为Artesaos\Defender\Role
的对象,或者是一个包含角色ids
的数组。
public function foo(Authenticable $user) { $role = Defender::findRole('admin'); // Returns an Artesao\Defender\Role $user->attachRole($role); // or $roles = [1, 2, 3]; // Using an array of ids $user->attachRole($roles); }
公开函数 detachRole($role)
:
从用户中解除角色 $role
(与 attachRole()
相反)。
public function foo(Authenticable $user) { $role = Defender::findRole('admin'); // Returns an Artesao\Defender\Role $user->detachRole($role); // ou $roles = [1, 2, 3]; // Using an array of ids $user->detachRole($roles); }
公开函数 syncRoles(array $roles = array())
:
这与 attachRole()
方法类似,但方法运行后,只有数组 $roles
中的角色会存在于关系中。$roles
是所需角色的 ids
的数组。
public function foo(Authenticable $user) { $roles = [1, 2, 3]; // Using an array of ids $user->syncRoles($roles); }
公开函数 attachPermission($permission, array $options = array())
:
将用户附加到权限 $permission
。变量 $permission
是 Artesaos\Defender\Permission
类的实例。
public function foo(Authenticable $user) { $permission = Defender::findPermission('user.create'); $user->attachPermission($permission, [ 'value' => true // true = has the permission, false = doesn't have the permission, ]); }
公开函数 detachPermission($permission)
:
从用户中移除权限 $permission
。变量 $permission
可能是 Artesaos\Defender\Permission
类的实例或包含要移除权限的 ids
的数组。
public function foo(Authenticable $user) { $permission = Defender::findPermission('user.create'); $user->detachPermission($permission); // or $permissions = [1, 3]; $user->detachPermission($permissions); }
公开函数 syncPermissions(array $permissions)
:
这与 syncRoles
方法类似,但方法运行后,只有数组 $permissions
中的角色会存在于关系中。
public function foo(Authenticable $user) { $permissions = [ 1 => ['value' => false], 2 => ['value' => true, 3 => ['value' => true] ]; $user->syncPermissions($permissions); }
公开函数 revokePermissions()
:
移除所有用户权限。
public function foo(Authenticable $user) { $user->revokePermissions(); }
公开函数 revokeExpiredPermissions()
:
从用户中移除所有临时过期的权限。关于临时权限的更多信息见下文。
public function foo(Authenticable $user) { $user->revokeExpiredPermissions(); }
临时权限
Defender 最酷的功能之一是向组或用户添加临时权限。
例如
用户 John 属于角色 'admins',但是我想临时移除 John 创建新用户的权限
在这种情况下,我们需要附加一个值为 false
的权限,明确禁止用户执行该操作。您必须添加此权限,并带有 false
值,因为默认情况下,用户权限继承自其角色的权限。当您分配用户权限时,这始终具有优先级。
例如。以下我们撤销了用户在 7 天内的 user.create
权限。
public function foo() { $userX = App\User::find(3); $permission = Defender::findPermission('user.create'); $userX->attachPermission($permission, [ 'value' => false, // false means that he will not have the permission, 'expires' => \Carbon\Carbon::now()->addDays(7) // Set the permission's expiration date ]); }
7 天后,用户将重新获得权限。
允许用户在一定时间内执行某些操作。
要允许用户在一段时间内临时访问执行特定操作,只需设置 expires
键。默认情况下,value
键将为 true
。
public function foo() { $user = App\User::find(1); $permission = Defender::findPermission('user.create'); $user->attachPermission($permission, [ 'expires' => \Carbon\Carbon::now()->addDays(7) ]; }
还可以扩展现有的临时权限:只需使用 $user->extendPermission($permissionName, array $options)
方法。
使用自定义角色和权限模型
要使用自己的角色和权限模型类,首先在 defender.php
配置文件中设置 role_model
和 permission_model
键。
以下是如何使用 jenssegers/laravel-mongodb 驱动程序在 MongoDB 中实现角色和权限模型的两个示例
<?php // Role model namespace App; use Jenssegers\Mongodb\Eloquent\Model; use Artesaos\Defender\Traits\Models\Role; use Artesaos\Defender\Contracts\Role as RoleInterface; /** * Class Role. */ class Role extends Model implements RoleInterface { use Role; }
<?php // Permission model namespace App; use Jenssegers\Mongodb\Eloquent\Model; use Artesaos\Defender\Traits\Models\Permission; use Artesaos\Defender\Contracts\Permission as PermissionInterface; /** * Class Permission. */ class Permission extends Model implements PermissionInterface { use Permission; }
您必须使用正确的特性,并且每个类都必须实现相应的接口合同。