genesis-tecnologia/laravel-defender

Laravel 的角色与权限

dev-master 2023-11-23 13:38 UTC

This package is auto-updated.

Last update: 2024-09-23 15:51: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
        \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。变量 $permissionGenesisTecnologia\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_modelpermission_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;    
    }

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