ligadaweb/defender

Laravel 的角色与权限

v1.0 2024-07-03 19:08 UTC

This package is auto-updated.

Last update: 2024-09-03 19:51:06 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

使用生成器或 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。变量 $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,
    ]);
}
公开函数 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_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;    
    }

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