laravel-permission 安装
- php版本:7.4
- laravel版本:8.54
1
composer require spatie/laravel-permission
可以直接参考官方文档。
配置
1、发布配置文件
1
2
# 这条命令会在 config 下增加一个 permission.php 的配置文件
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
2、注册 Autoloaded Service Providers
- config/app.php
1
2
3
4
5
6
7
return [
...
'providers' => [
// ...
Spatie\Permission\PermissionServiceProvider::class,
]
];
3、运行数据库迁移
1
php artisan migrate
这里的表结构都可以根据业务进行调整。
比如,我们和前端 antd vue pro 框架对接,需要返回用户的菜单和权限,那么在 permission 表中,就可以增加【是否菜单:is_menu】字段,来区分菜单权限和普通权限。
- 需要排序,就增加对应的排序字段;
- 需要图标icon,就增加对应储存icon的字段;
- 需要层级结构,就增加父级id的字段;
- …
数据库初始化时创建权限
在 database/seeders/DatabaseSeeder.php
中的 run()
方法中,可以创建系统默认权限、角色和用户。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
// 重置角色和权限的缓存
app()['cache']->forget('spatie.permission.cache');
// 创建权限(这里储存 xx.xxx.xx 的英文格式,多语言由前端实现)
// menu 创建菜单 和 权限
$user = Permission::create([
'name' => 'menu.user.manage',
'is_menu' => '1',
'show' => '1',
'parent_id' => '0',
'parent_ids' => ',0,',
'icon' => 'user',
'sort' => '100',
'component' => 'user/UserList',
'path' => '/user/list'
]);
Permission::create([
'name' => 'menu.user.manage^add',
'parent_id' => $user->id,
'parent_ids' => ',0,'.$user->id.',' // ",0,{$user->id}," 这种写法也可以,需要使用双引号
]);
Permission::create([
'name' => 'menu.user.manage^list',
'parent_id' => $user->id,
'parent_ids' => ',0,'.$user->id.','
]);
Permission::create([
'name' => 'menu.user.manage^info',
'parent_id' => $user->id,
'parent_ids' => ',0,'.$user->id.','
]);
Permission::create([
'name' => 'menu.user.manage^edit',
'parent_id' => $user->id,
'parent_ids' => ',0,'.$user->id.','
]);
Permission::create([
'name' => 'menu.user.manage^delete',
'parent_id' => $user->id,
'parent_ids' => ',0,'.$user->id.','
]);
// role 创建角色并赋予已创建的权限
$role = Role::create([
'name' => 'super',
'show_name' => 'superAdmin',
'sort' => '1',
]);
$role->givePermissionTo(Permission::all());
// create admin 创建管理员,分配角色
$user = Users::create([
'name' => 'admin',
'display_name' => '系统管理员',
'department_id' => $department->id,
'password' => bcrypt(Constants::DEFAULT_PASSWORD),
]);
$user->assignRole('super');
资源路由使用 permission 中间件
此软件包附带 RoleMiddleware 和 PermissionMiddleware 中间件。 你可以将它们添加到你的 app/Http/Kernel.php 文件中。
1
2
3
4
5
6
protected $routeMiddleware = [
// ...
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
然后,你可以使用中间件规则保护你的路由:
1
2
3
4
5
6
7
8
9
10
11
Route::group(['middleware' => ['role:super-admin']], function () {
//
});
Route::group(['middleware' => ['permission:publish articles']], function () {
//
});
Route::group(['middleware' => ['role:super-admin','permission:publish articles']], function () {
//
});
但是,当路由使用 apiResources 时,对不同路由需要进行权限控制,我们不能直接在 Route 中使用中间件,因为不同的请求方式,我们需要验证不同的权限。(post验证有无【新增】权限,put和patch验证有无【编辑】权限,……)
1
2
3
4
5
6
7
// 角色、部门、用户、字典
Route::apiResources([
'roles' => \App\Http\Controllers\Api\v1\RoleController::class,
'departments' => \App\Http\Controllers\Api\v1\DepartmentController::class,
'users' => \App\Http\Controllers\Api\v1\UserController::class,
'dictionaries' => \App\Http\Controllers\Api\v1\DictController::class,
]);
在 Laravue框架 的文档中,找到如下处理办法:
1
2
3
4
5
# File: routes/api.php
// All api requests to categories need "manage category" permission
Route::apiResource('categories', 'CategoryController')->middleware('permission:manage category');
// Listing category will require "view category" or "manage category"
Route::get('categories', 'CategoryController@index')->name('categories.index')->middleware('permission:view category|manage category');
即 额外定义对应路由中间件。个人觉得这样虽然可以实现功能,但破坏了 route 的美感,相当于不使用 apiResources
,手动定义了一遍全部的资源路由。
最后,研究中间件的时候,终于找到了一种我可以接受的解决方案:在 controller 中定义中间件。
1
2
3
4
5
6
7
8
// 权限配置
public function __construct()
{
// Middleware only applied to these methods
$this->middleware('permission:publish articles', ['only' => [
'update' // Could add bunch of more methods too
]]);
}
与 jwt 一起使用
因为 jwt 的 用户模型使用的 User 继承 Authenticatable,使用 laravel-permission 的 HasRoles Traits 也无法正常工作。
1
2
3
4
5
class User extends Authenticatable implements JWTSubject
{
...
}
这里使用继承 Model 的 Users,可以正常使用 使用 laravel-permission 的 HasRoles Traits。
1
2
3
4
class Users extends Model
{
...
}
经过调试定位,决定重写 permission 中间件。在 \App\Http\Middleware\
下创建 Permission 中间件,继承 laravel-permission 的 PermissionMiddleware。
修改用户实例,并使用 hasPermissionTo 判断权限。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace App\Http\Middleware;
use App\Models\Users;
use Spatie\Permission\Exceptions\UnauthorizedException;
use Closure;
use Spatie\Permission\Middlewares\PermissionMiddleware as Middleware;
class Permission extends Middleware
{
public function handle($request, Closure $next, $permission, $guard = null)
{
$authGuard = app('auth')->guard($guard);
if ($authGuard->guest()) {
throw UnauthorizedException::notLoggedIn();
}
$user = Users::find($authGuard->user()->id);
$permissions = is_array($permission)
? $permission
: explode('|', $permission);
foreach ($permissions as $permission) {
if ($user->hasPermissionTo($permission)) {
return $next($request);
}
}
throw UnauthorizedException::forPermissions($permissions);
}
}
最后,app/Http/Kernel.php 文件中,重新添加新的 permission 中间件
1
2
3
4
5
6
7
8
protected $routeMiddleware = [
// ...
// 'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
// 'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
// 'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
'permission' => \App\Http\Middleware\Permission::class,
];