artesaos/defender

Laravel 的角色和权限

安装: 155 670

依赖者: 3

建议者: 0

安全: 0

星标: 442

关注者: 43

分支: 97

开放问题: 31

v0.12.0 2022-03-14 12:18 UTC

README

Defender 是 Laravel 5 / 6 / 7 / 8 / 9 (单认证) 的访问控制列表 (ACL) 解决方案。 (不兼容多认证)
考虑到安全和易用性,本项目旨在为您提供一个安全的方式来控制应用程序访问,同时不失编码的乐趣。

当前构建状态

Build Status Code Climate StyleCI

统计数据

Latest Stable Version Latest Unstable Version License Total Downloads Monthly Downloads Daily Downloads

欢迎贡献

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

使用 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及以上版本,则可以使用中间件参数。

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的Blade扩展用于使用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):

通过角色IDroleId在数据库中查找角色。

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 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);
}
public function 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);
}
public function 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);
}
public function attachPermission($permission, array $options = array()):

将用户附加到权限 $permission。变量 $permissionArtesaos\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,
    ]);
}
public function 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);
}
public function syncPermissions(array $permissions):

这与 syncRoles 方法类似,但在方法运行后,只有数组 $permissions 中的角色将存在于关系中。

public function foo(Authenticable $user)
{
    $permissions = [
        1 => ['value' => false],
        2 => ['value' => true,
        3 => ['value' => true]
    ];

    $user->syncPermissions($permissions);
}
public function revokePermissions():

移除所有用户权限。

public function foo(Authenticable $user)
{
    $user->revokePermissions();
}
public function 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_modelpermission_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;    
    }

您必须使用正确的特性和每个类都必须实现相应的接口合约。