willypuzzle/laravel-permission

Laravel 5.4及以上版本的权限和部分处理

3.1.1 2018-12-14 11:51 UTC

README

此包允许您在数据库中管理用户权限和角色。这是spatie/laravel-permission的修改版本

安装后,您可以执行以下操作

// Adding permissions to a user dividing them by section
$user->givePermissionTo('edit articles', 'blog');

// Adding permissions via a role
$user->assignRole('writer');

and always by section
$role->givePermissionTo('edit articles', 'blog');

如果您使用多个守卫,我们也有相应的支持。每个守卫都将有自己的权限和角色集合,可以分配给守卫的用户。请参阅README中的使用多个守卫部分。

由于所有权限都将注册在Laravel的gate上,因此您可以使用Laravel的默认can函数测试用户是否有权限

$arguments['section'] = 'blog';
$user->can('edit articles', $arguments);

请记住,与角色或用户部分关联的权限始终是必须的。

安装

此包可用于Laravel 5.4或更高版本。

您可以通过composer安装此包

composer require willypuzzle/laravel-permission

在Laravel 5.5中,服务提供程序将自动注册。在框架的旧版本中,只需将服务提供程序添加到config/app.php文件中即可

'providers' => [
    // ...
    Idsign\Permission\PermissionServiceProvider::class,
];

您可以使用以下命令发布迁移:

php artisan vendor:publish --provider="Idsign\Permission\PermissionServiceProvider" --tag="migrations"

迁移发布后,您可以通过运行迁移来创建角色和权限表

php artisan migrate

您可以使用以下命令发布配置文件:

php artisan vendor:publish --provider="Idsign\Permission\PermissionServiceProvider" --tag="config"

发布后,config/permission.php配置文件包含以下内容

return [

    'models' => [

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * Eloquent model should be used to retrieve your permissions. Of course, it
         * is often just the "Permission" model but you may use whatever you like.
         *
         * The model you want to use as a Permission model needs to implement the
         * `Idsign\Permission\Contracts\Permission` contract.
         */

        'permission' => Idsign\Permission\Models\Permission::class,

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * Eloquent model should be used to retrieve your roles. Of course, it
         * is often just the "Role" model but you may use whatever you like.
         *
         * The model you want to use as a Role model needs to implement the
         * `Idsign\Permission\Contracts\Role` contract.
         */

        'role' => Idsign\Permission\Models\Role::class,

    ],

    'table_names' => [

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your roles. We have chosen a basic
         * default value but you may easily change it to any table you like.
         */

        'roles' => 'roles',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your permissions. We have chosen a basic
         * default value but you may easily change it to any table you like.
         */

        'permissions' => 'permissions',
        
        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your sections. We have chosen a basic
         * default value but you may easily change it to any table you like.
         */
        
        'sections' => 'sections',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your models permissions. We have chosen a
         * basic default value but you may easily change it to any table you like.
         */

        'model_has_permissions' => 'model_has_permissions',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your models roles. We have chosen a
         * basic default value but you may easily change it to any table you like.
         */

        'model_has_roles' => 'model_has_roles',

        /*
         * When using the "HasRoles" trait from this package, we need to know which
         * table should be used to retrieve your roles permissions. We have chosen a
         * basic default value but you may easily change it to any table you like.
         */

        'role_has_permissions' => 'role_has_permissions',
    ],

    /*
     * By default all permissions will be cached for 24 hours unless a permission or
     * role is updated. Then the cache will be flushed immediately.
     */

    'cache_expiration_time' => 60 * 24,
];

用法

首先,将Idsign\Permission\Traits\HasRoles特质添加到您的User模型中

use Illuminate\Foundation\Auth\User as Authenticatable;
use Idsign\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;

    // ...
}
  • 请注意,如果您需要将HasRoles特质与另一个模型(例如Page)一起使用,也需要将protected $guard_name = 'web';添加到该模型中,否则您将得到一个错误
use Illuminate\Database\Eloquent\Model;
use Idsign\Permission\Traits\HasRoles;

class Page extends Model
{
   use HasRoles;

   protected $guard_name = 'web'; // or whatever guard you want to use

   // ...
}

此包允许用户通过部分关联权限和角色。每个角色都与多个部分相关联,每个角色的每个部分都与权限相关联。一个RoleSectionPermission是常规Eloquent模型。它们需要name并且可以像这样创建

use Idsign\Permission\Models\Role;
use Idsign\Permission\Models\Permission;
use Idsign\Permission\Models\Section;

$role = Role::create(['name' => 'writer']);
$permission = Permission::create(['name' => 'edit articles']);
$section = Section::create(['name' => 'blog']);

如果您使用多个守卫,则还需要设置guard_name属性。请参阅README中的使用多个守卫部分。

HasRoles特质为您模型添加了Eloquent关系,您可以直接访问或用作基础查询

// get a list of all permissions directly assigned to the user
$permissions = $user->getPermissions('blog');//you have to pass the section

// get all permissions inherited by the user via roles
$permissions = $user->getAllPermissions('blog');

// get a collection of all defined roles
$roles = $user->getRoleNames(); // Returns a collection

HasRoles特质还为您模型添加了一个role作用域,以将查询范围到特定的角色

$users = User::role('writer')->get(); // Returns only users with the role 'writer'

role作用域可以接受字符串、\Idsign\Permission\Models\Role对象或\Illuminate\Support\Collection对象。

使用“直接”权限(下面将介绍如何同时使用角色和权限)

可以将权限授予任何用户

$user->givePermissionTo('edit articles', 'blog');//always indicate the section

// You may also pass an array
$user->givePermissionTo(['edit articles', 'delete articles'], 'blog');

可以从用户中撤销权限

$user->revokePermissionTo('edit articles', 'blog');

或者一次性撤销和添加新权限

$user->syncPermissions(['edit articles', 'delete articles'], 'blog');

您可以测试用户是否有权限

$user->hasPermissionTo('edit articles', 'blog');

...或者测试用户是否有多个权限

$user->hasAnyPermission(['edit articles', 'publish articles', 'unpublish articles'], 'blog');

保存的权限将与默认守卫的Illuminate\Auth\Access\Gate类一起注册。因此,您可以使用Laravel的默认can函数测试用户是否有权限

$arguments['section'] = 'blog';
$user->can('edit articles', $arguments);

通过角色使用权限

可以将角色分配给任何用户

$user->assignRole('writer');

// You can also assign multiple roles at once
$user->assignRole('writer', 'admin');
// or as an array
$user->assignRole(['writer', 'admin']);

可以从用户中移除角色

$user->removeRole('writer');

角色也可以进行同步

// All current roles will be removed from the user and replaced by the array given
$user->syncRoles(['writer', 'admin']);

可以确定用户是否具有某个角色

$user->hasRole('writer');

还可以确定用户是否具有给定列表中的任何角色

$user->hasAnyRole(Role::all());

还可以确定用户是否具有给定列表中的所有角色

$user->hasAllRoles(Role::all());

函数 assignRolehasRolehasAnyRolehasAllRolesremoveRole 可以接受一个字符串、一个 \Idsign\Permission\Models\Role 对象或一个 Illuminate\Support\Collection 对象。

可以将权限赋予一个角色

$role->givePermissionTo('edit articles', 'blog');

可以确定角色是否具有某个权限

$role->hasPermissionTo('edit articles', 'blog');

可以从角色中撤销权限

$role->revokePermissionTo('edit articles', 'blog');

函数 givePermissionTorevokePermissionTo 可以接受一个字符串或一个 Idsign\Permission\Models\Permission 对象,以及一个字符串或一个 Idsign\Permission\Models\Section 作为部分。

权限会自动从角色继承。此外,还可以为用户分配单个权限。例如

$role = Role::findByName('writer');
$role->givePermissionTo('edit articles', 'blog');

$user->assignRole('writer');

$user->givePermissionTo('delete articles', 'blog');

在上面的例子中,一个角色被赋予编辑文章的权限,并且这个角色被分配给了一个用户。现在该用户可以编辑文章,并且还可以删除文章。'删除文章'的权限是用户的直接权限,因为它直接分配给了他们。当我们调用 $user->hasDirectPermission('delete articles', 'blog') 时,它返回 true,但 false 对于 $user->hasDirectPermission('edit articles', 'blog')

此方法在为应用程序中的角色和用户构建设置权限的表单时非常有用,并且希望限制或更改用户的角色继承权限,即只允许更改用户的直接权限。

可以列出所有这些权限

// Direct permissions
$user->getDirectPermissions('blog')

// Permissions inherited from the user's roles
$user->getPermissionsViaRoles('blog');

// All permissions which apply on the user (inherited and direct)
$user->getAllPermissions('blog');

所有这些响应都是 Idsign\Permission\Models\Permission 对象的集合。

如果我们遵循前面的例子,第一个响应将是一个包含 删除文章 权限的集合,第二个将是一个包含 编辑文章 权限的集合,第三个将包含两者。

获取用户的权限树

获取特定用户的权限树(权限按部分划分)可能很有用。为了做到这一点,您可以使用 HasRole 特性的 $user->getPermissionsTree() 方法。

使用 Blade 指令

此包还添加了 Blade 指令,用于验证当前登录用户是否具有给定列表中的所有或任何角色。

可选地,您可以将要执行的检查的 guard 作为第二个参数传入。

Blade 和角色

测试特定角色

@role('writer')
    I am a writer!
@else
    I am not a writer...
@endrole

与以下相同

@hasrole('writer')
    I am a writer!
@else
    I am not a writer...
@endhasrole

测试列表中的任何角色

@hasanyrole($collectionOfRoles)
    I have one or more of these roles!
@else
    I have none of these roles...
@endhasanyrole
// or
@hasanyrole('writer|admin')
    I am either a writer or an admin or both!
@else
    I have none of these roles...
@endhasanyrole

测试所有角色

@hasallroles($collectionOfRoles)
    I have all of these roles!
@else
    I do not have all of these roles...
@endhasallroles
// or
@hasallroles('writer|admin')
    I am both a writer and an admin!
@else
    I do not have all of these roles...
@endhasallroles

Blade 和权限

此包不添加任何特定于权限的 Blade 指令。相反,使用 Laravel 的原生 @can 指令来检查用户是否具有特定权限。

@can('edit articles')
  //
@endcan

@if(auth()->user()->can('edit articles') && $some_other_condition)
  //
@endif

使用多个守卫

使用默认的 Laravel 身份验证配置时,上述所有方法都将按原样工作,无需额外配置。

然而,当使用多个守卫时,它们将像命名空间一样作用于您的权限和角色。这意味着每个守卫都有自己的权限和角色集,可以分配给其用户模型。

使用多个守卫的权限和角色

默认情况下,默认守卫(config('auth.defaults.guard'))将用作新权限和角色的守卫。当为特定守卫创建权限和角色时,您必须在模型上指定其 guard_name

// Create a superadmin role for the admin users
$role = Role::create(['guard_name' => 'admin', 'name' => 'superadmin']);

// Define a `publish articles` permission for the admin users belonging to the admin guard
$permission = Permission::create(['guard_name' => 'admin', 'name' => 'publish articles']);

// Define a *different* `publish articles` permission for the regular users belonging to the web guard
$permission = Permission::create(['guard_name' => 'web', 'name' => 'publish articles']);

检查用户是否有特定守卫的权限

$user->hasPermissionTo('publish articles', 'blog', 'admin');

将权限和角色分配给守卫用户

您可以使用上述描述的方法通过角色分配权限和角色给用户,如使用通过角色的权限。只需确保权限或角色上的 guard_name 与用户的守卫匹配,否则将抛出 GuardDoesNotMatch 异常。

使用具有多个守卫的blade指令

您可以通过将希望使用的守卫作为指令的第二个参数传入来使用使用blade指令中列出的所有blade指令。

@role('super-admin', 'admin')
    I am a super-admin!
@else
    I am not a super-admin...
@endrole

使用中间件

此包附带RoleMiddlewarePermissionMiddleware中间件。您可以在app/Http/Kernel.php文件中添加它们。

protected $routeMiddleware = [
    // ...
    'role' => \Idsign\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Idsign\Permission\Middlewares\PermissionMiddleware::class,
];

然后您可以使用中间件规则来保护您的路由。

Route::group(['middleware' => ['role:super-admin']], function () {
    //
});

Route::group(['middleware' => ['permission:publish articles:blog']], function () {
    //In case of permission Make sure to pass section separated by : or a exception will be raised.
});

Route::group(['middleware' => ['role:super-admin','permission:publish articles']], function () {
    //
});

或者,您可以使用竖线|(管道)字符分隔多个角色或权限。

Route::group(['middleware' => ['role:super-admin|writer']], function () {
    //
});

Route::group(['middleware' => ['permission:publish articles:blog|edit articles:blog']], function () {
    //
});

您可以通过在构造函数中设置所需的中间件以类似的方式保护控制器。

public function __construct()
{
    $this->middleware(['role:super-admin','permission:publish articles:blog|edit articles:blog']);
}

您可以使用\Idsign\Permission\Middlewares\CrudMiddleware保护CRUD路由,有关更多信息,请参阅配置文件。

捕获角色和权限失败

如果您想覆盖默认的403响应,您可以使用应用程序的异常处理器捕获UnauthorizedException

public function render($request, Exception $exception)
{
    if ($exception instanceof \Idsign\Permission\Exceptions\UnauthorizedException) {
        // Code here ...
    }

    return parent::render($request, $exception);
}

状态管理

用户

您可以为用户状态管理设置参数(有关更多信息,请参阅配置)。您可以调用isEnabled方法(HasRole特质的实例)来检查用户是否启用。如果用户被禁用,则不允许用户进入任何角色的任何部分。

状态管理也适用于角色和权限,您必须在数据库中设置状态字段,使用Role和Permission合约中的常量来检索值。

当权限的状态被禁用时,任何用户都无法访问该权限下的任何请求。当角色的状态被禁用时,对该角色关联的任何权限的请求都将不允许。对部分也是同样的。

使用Artisan命令

您可以使用Artisan命令从控制台创建角色或权限。

php artisan permission:create-role writer
php artisan permission:create-permission 'edit articles'

当为特定的守卫创建权限和角色时,您可以指定守卫名称作为第二个参数。

php artisan permission:create-role writer web
php artisan permission:create-permission 'edit articles' web

单元测试

在您应用程序的测试中,如果您没有在测试setUp()中将角色和权限作为一部分进行播种,那么您可能会遇到一个鸡生蛋的问题,即角色和权限没有被与门注册(因为您的测试在门注册之后创建了它们)。解决这个问题很简单:在您的测试中简单地添加一个setUp()指令来重新注册权限,如下所示

    public function setUp()
    {
        // first include all the normal setUp operations
        parent::setUp();

        // now re-register all the roles and permissions
        $this->app->make(\Idsign\Permission\PermissionRegistrar::class)->registerPermissions();
    }

数据库播种

关于数据库播种的两个注意事项

  1. 在播种之前最好刷新idsign.permission.cache.permissionsidsign.permission.cache.sections,以避免缓存冲突错误。这可以通过Artisan命令(参见故障排除:缓存部分)或直接在播种类中完成(参见下面的示例)。

  2. 下面是一个示例播种器,它清除缓存,创建权限,然后分配权限给角色

    use Illuminate\Database\Seeder;
    use Idsign\Permission\Models\Role;
    use Idsign\Permission\Models\Permission;
    use Idsign\Permission\Models\Section;
    
    class RolesAndPermissionsSeeder extends Seeder
    {
        public function run()
    	{
        	// Reset cached roles and permissions
            app()['cache']->forget('idsign.permission.cache.permissions');
            app()['cache']->forget('idsign.permission.cache.sections');
    
            // create permissions
            Permission::create(['name' => 'edit articles']);
            Permission::create(['name' => 'delete articles']);
            Permission::create(['name' => 'publish articles']);
            Permission::create(['name' => 'unpublish articles']);
        
            Section::create(['blog' => 'unpublish articles']);
    
            // create roles and assign existing permissions
            $role = Role::create(['name' => 'writer']);
            $role->givePermissionTo('edit articles', 'blog');
            $role->givePermissionTo('delete articles', 'blog');
    
            $role = Role::create(['name' => 'admin']);
            $role->givePermissionTo('publish articles', 'blog');
            $role->givePermissionTo('unpublish articles', 'blog');
        }
    }

扩展

如果您需要扩展或替换现有的RolePermission模型,您只需注意以下事项

  • 您的Role模型需要实现Idsign\Permission\Contracts\Role合约
  • 您的Permission模型需要实现Idsign\Permission\Contracts\Permission合约
  • 您的Section模型需要实现Idsign\Permission\Contracts\Section合约
  • 您可以使用此命令发布配置
php artisan vendor:publish --provider="Idsign\Permission\PermissionServiceProvider" --tag="config"

并更新models.rolemodels.permissionmodels.section

缓存

角色和权限数据被缓存以提高性能。

当您使用提供的用于操作角色和权限的方法时,缓存将自动为您重置

$user->assignRole('writer');
$user->removeRole('writer');
$user->syncRoles(params);
$role->givePermissionTo('edit articles', 'blog');
$role->revokePermissionTo('edit articles', 'blog');
$role->syncPermissions(params, 'blog');

但是,如果您直接在数据库中操作权限/角色数据而不是调用提供的方法,除非您手动重置缓存,否则您将看不到应用程序中的更改。

手动缓存重置

要手动重置此包的缓存,请运行

php artisan cache:forget idsign.permission.cache.permissions
php artisan cache:forget idsign.permission.cache.sections

缓存标识符

提示:如果您正在使用例如 redismemcached 的缓存服务,并且您的服务器上运行着其他站点,您可能会遇到缓存冲突。明智的做法是在 /config/cache.php 中为每个应用程序设置唯一的缓存 prefix。这将防止其他应用程序意外使用/更改您的缓存数据。

测试

composer test

变更日志

请参阅 CHANGELOG 获取有关最近更改的更多信息。

贡献

请参阅 CONTRIBUTING 了解详细信息。

安全

如果您发现任何安全相关的问题,请通过电子邮件 domenico.rizzo@gmail.com 而不是使用问题跟踪器。

鸣谢

许可协议

MIT 许可协议 (MIT)。请参阅 许可文件 获取更多信息。