genesis-tecnologia / laravel-defender
Laravel 的角色与权限
Requires
- php: >=7.2.0 || ^8.0
- laravel/framework: ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0
Requires (Dev)
- fakerphp/faker: ^1.0
- friendsofphp/php-cs-fixer: ~2.11
- orchestra/database: ^4.0 || ^5.0 || ^6.0 || ^7.0
- orchestra/testbench: ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0
- phpunit/phpunit: ^8.0 || ^9.0 || ^10.0
This package is auto-updated.
Last update: 2024-09-23 15:51:18 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 \GenesisTecnologia\Defender\Providers\DefenderServiceProvider::class, ], // file END ommited
3. 用户类
在您的用户类中,添加 traits GenesisTecnologia\Defender\Traits\HasDefender 以启用权限和角色的创建
<?php namespace App; use Illuminate\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use GenesisTecnologia\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 GenesisTecnologia\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' => \GenesisTecnologia\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' => \GenesisTecnologia\Defender\Middlewares\NeedsPermissionMiddleware::class, // Simpler access control, uses only the groups 'needsRole' => \GenesisTecnologia\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
使用 seeder 或 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 的功能,您需要在您的用户模型(通常是 App\User)中添加 HasDefender 特性。
<?php namespace App; // Declaration of other omitted namespaces use GenesisTecnologia\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 可能是类型为 GenesisTecnologia\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 是 GenesisTecnologia\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 可能是 GenesisTecnologia\Defender\Permission 类的实例或包含要删除的权限 id 的数组。
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 GenesisTecnologia\Defender\Traits\Models\Role; use GenesisTecnologia\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 GenesisTecnologia\Defender\Traits\Models\Permission; use GenesisTecnologia\Defender\Contracts\Permission as PermissionInterface; /** * Class Permission. */ class Permission extends Model implements PermissionInterface { use Permission; }
您必须使用正确的特性和每个类都必须实现相应的接口合同。