laravel-permission-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Laravel Permission Development

Laravel Permission 开发指南

When to use this skill

何时使用该技能

Use this skill when working with authorization, roles, permissions, access control, middleware guards, or Blade permission directives using spatie/laravel-permission.
当你使用spatie/laravel-permission处理授权、角色、权限、访问控制、中间件守卫或Blade权限指令时,可以使用本技能。

Core Concepts

核心概念

  • Users have Roles, Roles have Permissions, Apps check Permissions (not Roles).
  • Direct permissions on users are an anti-pattern; assign permissions to roles instead.
  • Use
    $user->can('permission-name')
    for all authorization checks (supports Super Admin via Gate).
  • The
    HasRoles
    trait (which includes
    HasPermissions
    ) is added to User models.
  • 用户拥有角色,角色拥有权限,应用校验权限(而非角色)。
  • 直接给用户分配权限是反模式;应改为将权限分配给角色。
  • 所有授权校验都使用
    $user->can('permission-name')
    (通过Gate支持超级管理员)。
  • 需将
    HasRoles
    trait(包含
    HasPermissions
    )添加到User模型中。

Setup

配置步骤

Add the
HasRoles
trait to your User model:
php
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
}
HasRoles
trait添加到你的User模型中:
php
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
}

Creating Roles and Permissions

创建角色与权限

php
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

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

// findOrCreate is idempotent (safe for seeders)
$role = Role::findOrCreate('writer', 'web');
$permission = Permission::findOrCreate('edit articles', 'web');
php
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

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

// findOrCreate是幂等方法(适用于数据填充)
$role = Role::findOrCreate('writer', 'web');
$permission = Permission::findOrCreate('edit articles', 'web');

Assigning Roles and Permissions

分配角色与权限

php
// Assign roles to users
$user->assignRole('writer');
$user->assignRole('writer', 'admin');
$user->assignRole(['writer', 'admin']);
$user->syncRoles(['writer', 'admin']); // replaces all
$user->removeRole('writer');

// Assign permissions to roles (preferred)
$role->givePermissionTo('edit articles');
$role->givePermissionTo(['edit articles', 'delete articles']);
$role->syncPermissions(['edit articles', 'delete articles']);
$role->revokePermissionTo('edit articles');

// Reverse assignment
$permission->assignRole('writer');
$permission->syncRoles(['writer', 'editor']);
$permission->removeRole('writer');
php
// 给用户分配角色
$user->assignRole('writer');
$user->assignRole('writer', 'admin');
$user->assignRole(['writer', 'admin']);
$user->syncRoles(['writer', 'admin']); // 替换所有现有角色
$user->removeRole('writer');

// 给角色分配权限(推荐方式)
$role->givePermissionTo('edit articles');
$role->givePermissionTo(['edit articles', 'delete articles']);
$role->syncPermissions(['edit articles', 'delete articles']);
$role->revokePermissionTo('edit articles');

// 反向分配
$permission->assignRole('writer');
$permission->syncRoles(['writer', 'editor']);
$permission->removeRole('writer');

Checking Roles and Permissions

校验角色与权限

php
// Permission checks (preferred - supports Super Admin via Gate)
$user->can('edit articles');
$user->canAny(['edit articles', 'delete articles']);

// Direct package methods (bypass Gate, no Super Admin support)
$user->hasPermissionTo('edit articles');
$user->hasAnyPermission(['edit articles', 'publish articles']);
$user->hasAllPermissions(['edit articles', 'publish articles']);
$user->hasDirectPermission('edit articles');

// Role checks
$user->hasRole('writer');
$user->hasAnyRole(['writer', 'editor']);
$user->hasAllRoles(['writer', 'editor']);
$user->hasExactRoles(['writer', 'editor']);

// Get assigned roles and permissions
$user->getRoleNames();           // Collection of role name strings
$user->getPermissionNames();     // Collection of permission name strings
$user->getDirectPermissions();   // Direct permissions only
$user->getPermissionsViaRoles(); // Inherited via roles
$user->getAllPermissions();      // Both direct and inherited
php
// 权限校验(推荐方式 - 通过Gate支持超级管理员)
$user->can('edit articles');
$user->canAny(['edit articles', 'delete articles']);

// 直接调用包方法(绕过Gate,不支持超级管理员)
$user->hasPermissionTo('edit articles');
$user->hasAnyPermission(['edit articles', 'publish articles']);
$user->hasAllPermissions(['edit articles', 'publish articles']);
$user->hasDirectPermission('edit articles');

// 角色校验
$user->hasRole('writer');
$user->hasAnyRole(['writer', 'editor']);
$user->hasAllRoles(['writer', 'editor']);
$user->hasExactRoles(['writer', 'editor']);

// 获取已分配的角色与权限
$user->getRoleNames();           // 角色名称集合
$user->getPermissionNames();     // 权限名称集合
$user->getDirectPermissions();   // 仅直接分配的权限
$user->getPermissionsViaRoles(); // 通过角色继承的权限
$user->getAllPermissions();      // 直接分配与继承的所有权限

Query Scopes

查询作用域

php
$users = User::role('writer')->get();
$users = User::withoutRole('writer')->get();
$users = User::permission('edit articles')->get();
$users = User::withoutPermission('edit articles')->get();
php
$users = User::role('writer')->get();
$users = User::withoutRole('writer')->get();
$users = User::permission('edit articles')->get();
$users = User::withoutPermission('edit articles')->get();

Middleware

中间件

Register middleware aliases in
bootstrap/app.php
:
php
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
        'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
        'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
    ]);
})
Use in routes (pipe
|
for OR logic):
php
Route::middleware(['permission:edit articles'])->group(function () { ... });
Route::middleware(['role:manager|writer'])->group(function () { ... });
Route::middleware(['role_or_permission:manager|edit articles'])->group(function () { ... });

// With specific guard
Route::middleware(['role:manager,api'])->group(function () { ... });
For single permissions, Laravel's built-in
can
middleware also works:
php
Route::middleware(['can:edit articles'])->group(function () { ... });
bootstrap/app.php
中注册中间件别名:
php
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
        'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
        'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
    ]);
})
在路由中使用(使用竖线
|
表示或逻辑):
php
Route::middleware(['permission:edit articles'])->group(function () { ... });
Route::middleware(['role:manager|writer'])->group(function () { ... });
Route::middleware(['role_or_permission:manager|edit articles'])->group(function () { ... });

// 指定守卫
Route::middleware(['role:manager,api'])->group(function () { ... });
对于单个权限,Laravel内置的
can
中间件同样适用:
php
Route::middleware(['can:edit articles'])->group(function () { ... });

Blade Directives

Blade指令

Prefer
@can
(permission-based) over
@role
(role-based):
blade
@can('edit articles')
    {{-- User can edit articles (supports Super Admin) --}}
@endcan

@canany(['edit articles', 'delete articles'])
    {{-- User can do at least one --}}
@endcanany

@role('admin')
    {{-- Only use for super-admin type checks --}}
@endrole

@hasanyrole('writer|admin')
    {{-- Has writer or admin --}}
@endhasanyrole
优先使用
@can
(基于权限)而非
@role
(基于角色):
blade
@can('edit articles')
    {{-- 用户可编辑文章(支持超级管理员) --}}
@endcan

@canany(['edit articles', 'delete articles'])
    {{-- 用户至少拥有其中一项权限 --}}
@endcanany

@role('admin')
    {{-- 仅用于超级管理员类型的校验 --}}
@endrole

@hasanyrole('writer|admin')
    {{-- 拥有writer或admin角色 --}}
@endhasanyrole

Super Admin

超级管理员

Use
Gate::before
in
AppServiceProvider::boot()
:
php
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::before(function ($user, $ability) {
        return $user->hasRole('Super Admin') ? true : null;
    });
}
This makes
$user->can()
and
@can
always return true for Super Admins. Must return
null
(not
false
) to allow normal checks for other users.
AppServiceProvider::boot()
中使用
Gate::before
php
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::before(function ($user, $ability) {
        return $user->hasRole('Super Admin') ? true : null;
    });
}
这会让超级管理员的
$user->can()
@can
始终返回true。对于其他用户,必须返回
null
(而非
false
)以允许正常校验。

Policies

策略

Use
$user->can()
inside policy methods to check permissions:
php
class PostPolicy
{
    public function update(User $user, Post $post): bool
    {
        if ($user->can('edit all posts')) {
            return true;
        }

        return $user->can('edit own posts') && $user->id === $post->user_id;
    }
}
在策略方法中使用
$user->can()
来校验权限:
php
class PostPolicy
{
    public function update(User $user, Post $post): bool
    {
        if ($user->can('edit all posts')) {
            return true;
        }

        return $user->can('edit own posts') && $user->id === $post->user_id;
    }
}

Enums

枚举

php
enum RolesEnum: string
{
    case WRITER = 'writer';
    case EDITOR = 'editor';
}

enum PermissionsEnum: string
{
    case EDIT_POSTS = 'edit posts';
    case DELETE_POSTS = 'delete posts';
}

// Creation requires ->value
Permission::findOrCreate(PermissionsEnum::EDIT_POSTS->value, 'web');

// Most methods accept enums directly
$user->assignRole(RolesEnum::WRITER);
$user->hasRole(RolesEnum::WRITER);
$role->givePermissionTo(PermissionsEnum::EDIT_POSTS);
$user->hasPermissionTo(PermissionsEnum::EDIT_POSTS);
php
enum RolesEnum: string
{
    case WRITER = 'writer';
    case EDITOR = 'editor';
}

enum PermissionsEnum: string
{
    case EDIT_POSTS = 'edit posts';
    case DELETE_POSTS = 'delete posts';
}

// 创建时需使用->value
Permission::findOrCreate(PermissionsEnum::EDIT_POSTS->value, 'web');

// 大多数方法直接支持枚举
$user->assignRole(RolesEnum::WRITER);
$user->hasRole(RolesEnum::WRITER);
$role->givePermissionTo(PermissionsEnum::EDIT_POSTS);
$user->hasPermissionTo(PermissionsEnum::EDIT_POSTS);

Seeding

数据填充

Always flush the permission cache when seeding:
php
class RolesAndPermissionsSeeder extends Seeder
{
    public function run(): void
    {
        // Reset cache
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

        // Create permissions
        Permission::findOrCreate('edit articles', 'web');
        Permission::findOrCreate('delete articles', 'web');

        // Create roles and assign permissions
        Role::findOrCreate('writer', 'web')
            ->givePermissionTo(['edit articles']);

        Role::findOrCreate('admin', 'web')
            ->givePermissionTo(Permission::all());
    }
}
数据填充时务必刷新权限缓存:
php
class RolesAndPermissionsSeeder extends Seeder
{
    public function run(): void
    {
        // 重置缓存
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

        // 创建权限
        Permission::findOrCreate('edit articles', 'web');
        Permission::findOrCreate('delete articles', 'web');

        // 创建角色并分配权限
        Role::findOrCreate('writer', 'web')
            ->givePermissionTo(['edit articles']);

        Role::findOrCreate('admin', 'web')
            ->givePermissionTo(Permission::all());
    }
}

Teams (Multi-Tenancy)

团队(多租户)

Enable in
config/permission.php
before running migrations:
php
'teams' => true,
Set the active team in middleware:
php
setPermissionsTeamId($teamId);
When switching teams, unset cached relations:
php
$user->unsetRelation('roles')->unsetRelation('permissions');
在运行迁移前,在
config/permission.php
中启用:
php
'teams' => true,
在中间件中设置当前活跃团队:
php
setPermissionsTeamId($teamId);
切换团队时,需清除缓存的关联关系:
php
$user->unsetRelation('roles')->unsetRelation('permissions');

Events

事件

Enable in
config/permission.php
:
php
'events_enabled' => true,
Available events:
RoleAttachedEvent
,
RoleDetachedEvent
,
PermissionAttachedEvent
,
PermissionDetachedEvent
in the
Spatie\Permission\Events
namespace.
config/permission.php
中启用:
php
'events_enabled' => true,
可用事件:
Spatie\Permission\Events
命名空间下的
RoleAttachedEvent
RoleDetachedEvent
PermissionAttachedEvent
PermissionDetachedEvent

Performance

性能优化

  • Permissions are cached automatically. The cache is flushed when roles/permissions change via package methods.
  • After direct DB operations, flush manually:
    app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions()
  • For bulk seeding, use
    Permission::insert()
    for speed, but flush the cache afterward.
  • 权限会自动缓存。当通过包方法修改角色/权限时,缓存会自动刷新。
  • 直接操作数据库后,需手动刷新缓存:
    app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions()
  • 批量填充数据时,使用
    Permission::insert()
    提升速度,但之后需刷新缓存。