在 Laravel 12 中使用 spatie/laravel-permission 进行路由权限管理的步骤和实战代码

在 Laravel 12 中使用 spatie/laravel-permission 进行路由权限管理,需要系统化的设计与实现。以下是详细步骤和实战代码:


1. 安装与配置

安装包
composer require spatie/laravel-permission
发布迁移文件
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
运行迁移
php artisan migrate
注册中间件(app/Http/Kernel.php
protected $routeMiddleware = [
    // ...
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];

2. 权限系统设计

权限规划表
权限标识 (Permission)描述路由示例
posts.view查看文章GET /posts
posts.create创建文章POST /posts
posts.edit编辑文章PUT /posts/{id}
posts.delete删除文章DELETE /posts/{id}
users.manage管理用户GET /admin/users
角色规划
  • Admin: 所有权限
  • Editor: posts.view, posts.create, posts.edit
  • Viewer: posts.view

3. 初始化权限和角色

创建权限播种器:

php artisan make:seeder PermissionSeeder
database/seeders/PermissionSeeder.php
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

public function run(): void
{
    // 重置缓存
    app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

    // 创建权限
    $permissions = [
        'posts.view',
        'posts.create',
        'posts.edit',
        'posts.delete',
        'users.manage'
    ];
    
    foreach ($permissions as $permission) {
        Permission::firstOrCreate(['name' => $permission]);
    }

    // 创建角色并分配权限
    $admin = Role::firstOrCreate(['name' => 'Admin']);
    $admin->givePermissionTo(Permission::all());

    $editor = Role::firstOrCreate(['name' => 'Editor']);
    $editor->givePermissionTo(['posts.view', 'posts.create', 'posts.edit']);

    $viewer = Role::firstOrCreate(['name' => 'Viewer']);
    $viewer->givePermissionTo('posts.view');
}

运行播种器:

php artisan db:seed --class=PermissionSeeder

4. 用户分配角色

在用户模型(app/Models/User.php)中添加 Trait:

use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
}

分配角色(例如在控制器中):

$user = User::find(1);
$user->assignRole('Admin');

5. 路由权限控制

路由配置(routes/web.php
use App\Http\Controllers\PostController;

// 公共路由
Route::get('/', [HomeController::class, 'index']);

// 需要认证的路由组
Route::middleware(['auth'])->group(function () {
    // 文章路由 - 权限控制
    Route::get('/posts', [PostController::class, 'index'])
        ->middleware('permission:posts.view');
    
    Route::get('/posts/create', [PostController::class, 'create'])
        ->middleware('permission:posts.create');
    
    Route::post('/posts', [PostController::class, 'store'])
        ->middleware('permission:posts.create');
    
    Route::get('/posts/{post}/edit', [PostController::class, 'edit'])
        ->middleware('permission:posts.edit');
    
    Route::put('/posts/{post}', [PostController::class, 'update'])
        ->middleware('permission:posts.edit');
    
    Route::delete('/posts/{post}', [PostController::class, 'destroy'])
        ->middleware('permission:posts.delete');

    // 管理员路由 - 角色控制
    Route::prefix('admin')->group(function () {
        Route::get('/users', [UserController::class, 'index'])
            ->middleware('role:Admin');
    });
});
高级用法:多条件检查
// 需满足角色或权限之一
Route::get('/dashboard', [DashboardController::class, 'index'])
    ->middleware('role_or_permission:Admin|posts.view');

// 同时检查多个权限
Route::get('/reports', [ReportController::class, 'index'])
    ->middleware('permission:posts.view,posts.edit');

6. 异常处理

自定义 403 页面(app/Exceptions/Handler.php):

use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

public function register(): void
{
    $this->renderable(function (AccessDeniedHttpException $e, $request) {
        return response()->view('errors.403', [], 403);
    });
}

创建视图 resources/views/errors/403.blade.php

@extends('layouts.app')
@section('content')
    <div class="alert alert-danger">
        您无权访问此页面!
    </div>
@endsection

7. 前端视图集成

在 Blade 模板中检查权限:

@can('posts.create')
    <a href="{{ route('posts.create') }}" class="btn btn-primary">
        创建文章
    </a>
@endcan

@role('Admin')
    <a href="/admin/users" class="btn btn-danger">
        用户管理
    </a>
@endrole

8. 实战技巧

缓存优化
# 权限变更后清除缓存
php artisan permission:cache-reset
中间件优先级

app/Http/Kernel.php 中确保中间件顺序:

protected $middlewarePriority = [
    \App\Http\Middleware\Authenticate::class,
    \Spatie\Permission\Middlewares\PermissionMiddleware::class, // 在认证之后
    // ...
];
控制台命令
# 查看用户权限
php artisan permission:show-permissions {userId}

# 同步角色权限
php artisan permission:create-role Editor --permissions="posts.view,posts.create"

9. 测试验证

创建测试用例:

php artisan make:test PostPermissionTest
tests/Feature/PostPermissionTest.php
public function test_editor_can_edit_post(): void
{
    $user = User::factory()->create()->assignRole('Editor');
    $post = Post::factory()->create();

    $response = $this->actingAs($user)
        ->put(route('posts.update', $post), [
            'title' => 'Updated Title'
        ]);

    $response->assertStatus(200);
    $this->assertDatabaseHas('posts', ['title' => 'Updated Title']);
}

public function test_viewer_cannot_delete_post(): void
{
    $user = User::factory()->create()->assignRole('Viewer');
    $post = Post::factory()->create();

    $response = $this->actingAs($user)
        ->delete(route('posts.destroy', $post));

    $response->assertForbidden();
}

10. 安全增强

防止 URL 参数篡改

在控制器中二次验证:

public function edit(Post $post)
{
    // 权限二次确认
    if (!auth()->user()->can('posts.edit')) {
        abort(403);
    }
    
    return view('posts.edit', compact('post'));
}
隐藏无权限元素

在 Blade 中使用 @can 确保前端安全:

@foreach($posts as $post)
    <div>
        {{ $post->title }}
        @can('posts.delete')
            <button @click="deletePost({{ $post->id }})">删除</button>
        @endcan
    </div>
@endforeach

最终目录结构

app/
├── Models/
│   └── User.php
├── Http/
│   ├── Controllers/
│   │   ├── PostController.php
│   │   └── UserController.php
│   └── Middleware/
database/
├── migrations/
│   └── [spatie_permission_tables]
└── seeders/
    └── PermissionSeeder.php
routes/
└── web.php
resources/
└── views/
    ├── posts/
    └── errors/
        └── 403.blade.php

通过以上设计,可实现精细化的路由权限控制。关键要点:

  1. 权限颗粒化:按操作定义权限标识
  2. 中间件组合:灵活使用 permissionrole
  3. 分层防御:路由中间件 + 控制器二次验证 + 前端隐藏
  4. 缓存管理:权限变更后及时清除缓存
  5. 自动化测试:确保权限逻辑正确性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值