ThinkPHP6/8 中间件开发与权限控制系统实战
Orion K Lv6

在现代Web应用开发中,中间件和权限控制系统是保障应用安全性和可维护性的重要组成部分。本文将深入探讨ThinkPHP6/8框架中的中间件开发技巧,并实现一套完整的RBAC权限控制系统。

中间件基础概念

中间件是一种过滤HTTP请求的机制,它可以在请求到达控制器之前或响应返回给客户端之前执行特定的逻辑。ThinkPHP6/8的中间件基于洋葱模型设计,支持前置和后置操作。

中间件执行流程

1
请求 -> 中间件1(前置) -> 中间件2(前置) -> 控制器 -> 中间件2(后置) -> 中间件1(后置) -> 响应

JWT认证中间件实现

1. 安装JWT扩展包

1
composer require thans/tp-jwt-auth

2. 生成JWT配置

1
php think jwt:create

3. 自定义JWT认证中间件

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?php
namespace app\middleware;

use thans\jwt\exception\JWTException;
use thans\jwt\exception\TokenBlacklistException;
use thans\jwt\exception\TokenBlacklistGracePeriodException;
use thans\jwt\exception\TokenExpiredException;
use thans\jwt\middleware\JWTAuth;
use think\exception\HttpException;
use think\facade\Log;
use think\Response;

/**
* JWT认证中间件
* 支持自动刷新token和用户信息注入
*/
class JWTAuthMiddleware extends JWTAuth
{
/**
* 处理请求
* @param \think\Request $request 请求对象
* @param \Closure $next 下一个中间件
* @return Response
* @throws HttpException
*/
public function handle($request, \Closure $next): Response
{
$token = null;

try {
// 验证token
$payload = $this->auth->auth();

} catch (TokenExpiredException $e) {
// token过期,尝试刷新
try {
$this->auth->setRefresh();
$token = $this->auth->refresh();
$payload = $this->auth->auth(false);

Log::info('Token自动刷新成功', [
'old_token' => $request->header('Authorization'),
'new_token' => $token
]);

} catch (TokenBlacklistGracePeriodException $e) {
// 在宽限期内,允许使用
$payload = $this->auth->auth(false);

} catch (JWTException $exception) {
Log::warning('Token刷新失败', [
'error' => $exception->getMessage(),
'token' => $request->header('Authorization')
]);
throw new HttpException(401, '认证失败,请重新登录');
}

} catch (TokenBlacklistGracePeriodException $e) {
// 在黑名单宽限期内
$payload = $this->auth->auth(false);

} catch (TokenBlacklistException $e) {
Log::warning('使用黑名单Token访问', [
'token' => $request->header('Authorization'),
'ip' => $request->ip(),
'url' => $request->url()
]);
throw new HttpException(401, '认证失败,请重新登录');

} catch (JWTException $e) {
Log::warning('JWT认证异常', [
'error' => $e->getMessage(),
'token' => $request->header('Authorization'),
'ip' => $request->ip()
]);
throw new HttpException(401, '认证失败:' . $e->getMessage());
}

// 将用户信息注入到请求中
$request->uid = $payload['uid']->getValue();
$request->userInfo = $payload['user_info'] ?? null;

// 执行下一个中间件
$response = $next($request);

// 如果有新token,添加到响应头
if (isset($token)) {
$this->setAuthentication($response, $token);
}

return $response;
}
}

4. 用户登录服务

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<?php
namespace app\service;

use app\model\User;
use thans\jwt\facade\JWTAuth;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Log;

/**
* 用户认证服务
*/
class AuthService
{
/**
* 用户登录
* @param string $username 用户名
* @param string $password 密码
* @param string $loginType 登录类型
* @return array
* @throws ValidateException
*/
public function login($username, $password, $loginType = 'web')
{
// 验证用户信息
$user = User::where('username', $username)
->where('status', 1)
->find();

if (!$user) {
throw new ValidateException('用户不存在或已被禁用');
}

if (!password_verify($password, $user->password)) {
// 记录登录失败
$this->recordLoginAttempt($username, false);
throw new ValidateException('密码错误');
}

// 检查登录失败次数
$this->checkLoginAttempts($username);

// 生成JWT token
$payload = [
'uid' => $user->id,
'username' => $user->username,
'login_type' => $loginType,
'login_time' => time(),
'user_info' => [
'nickname' => $user->nickname,
'avatar' => $user->avatar,
'email' => $user->email
]
];

$token = JWTAuth::builder($payload);

// 更新用户登录信息
$user->save([
'last_login_time' => time(),
'last_login_ip' => request()->ip()
]);

// 记录登录成功
$this->recordLoginAttempt($username, true);

// 清除登录失败记录
Cache::delete('login_attempts_' . $username);

Log::info('用户登录成功', [
'user_id' => $user->id,
'username' => $username,
'login_type' => $loginType,
'ip' => request()->ip()
]);

return [
'token' => 'Bearer ' . $token,
'user_info' => [
'id' => $user->id,
'username' => $user->username,
'nickname' => $user->nickname,
'avatar' => $user->avatar,
'email' => $user->email
],
'expires_in' => config('jwt.ttl') * 60
];
}

/**
* 用户登出
* @param string $token JWT token
* @return bool
*/
public function logout($token = null)
{
try {
if ($token) {
// 将token加入黑名单
JWTAuth::invalidate($token);
}

Log::info('用户登出成功', [
'user_id' => request()->uid ?? 0,
'ip' => request()->ip()
]);

return true;
} catch (\Exception $e) {
Log::error('用户登出失败', [
'error' => $e->getMessage(),
'token' => $token
]);
return false;
}
}

/**
* 刷新token
* @return array
*/
public function refreshToken()
{
try {
$newToken = JWTAuth::refresh();

return [
'token' => 'Bearer ' . $newToken,
'expires_in' => config('jwt.ttl') * 60
];
} catch (\Exception $e) {
throw new ValidateException('Token刷新失败:' . $e->getMessage());
}
}

/**
* 记录登录尝试
* @param string $username 用户名
* @param bool $success 是否成功
*/
private function recordLoginAttempt($username, $success)
{
$key = 'login_attempts_' . $username;
$attempts = Cache::get($key, []);

$attempts[] = [
'time' => time(),
'success' => $success,
'ip' => request()->ip()
];

// 只保留最近1小时的记录
$attempts = array_filter($attempts, function($attempt) {
return $attempt['time'] > (time() - 3600);
});

Cache::set($key, $attempts, 3600);
}

/**
* 检查登录失败次数
* @param string $username 用户名
* @throws ValidateException
*/
private function checkLoginAttempts($username)
{
$key = 'login_attempts_' . $username;
$attempts = Cache::get($key, []);

// 统计最近15分钟内的失败次数
$recentFailures = array_filter($attempts, function($attempt) {
return !$attempt['success'] && $attempt['time'] > (time() - 900);
});

if (count($recentFailures) >= 5) {
throw new ValidateException('登录失败次数过多,请15分钟后再试');
}
}
}

RBAC权限控制系统

1. 数据库设计

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
64
65
66
67
-- 用户表
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1正常 0禁用',
`last_login_time` int(11) DEFAULT NULL COMMENT '最后登录时间',
`last_login_ip` varchar(50) DEFAULT NULL COMMENT '最后登录IP',
`create_time` int(11) DEFAULT NULL,
`update_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 角色表
CREATE TABLE `roles` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '角色名称',
`description` varchar(255) DEFAULT NULL COMMENT '角色描述',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1正常 0禁用',
`sort` int(11) DEFAULT '0' COMMENT '排序',
`create_time` int(11) DEFAULT NULL,
`update_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';

-- 权限表
CREATE TABLE `permissions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '权限名称',
`rule` varchar(255) NOT NULL COMMENT '权限规则',
`type` tinyint(1) DEFAULT '1' COMMENT '类型:1菜单 2按钮',
`pid` int(11) DEFAULT '0' COMMENT '父级ID',
`icon` varchar(50) DEFAULT NULL COMMENT '图标',
`sort` int(11) DEFAULT '0' COMMENT '排序',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1正常 0禁用',
`create_time` int(11) DEFAULT NULL,
`update_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `pid` (`pid`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';

-- 用户角色关联表
CREATE TABLE `user_roles` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`role_id` int(11) NOT NULL COMMENT '角色ID',
`create_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_role` (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';

-- 角色权限关联表
CREATE TABLE `role_permissions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`role_id` int(11) NOT NULL COMMENT '角色ID',
`permission_id` int(11) NOT NULL COMMENT '权限ID',
`create_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `role_permission` (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';

2. RBAC权限中间件

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
<?php
namespace app\middleware;

use app\service\RbacService;
use think\exception\HttpException;
use think\facade\Log;
use think\Response;

/**
* RBAC权限控制中间件
*/
class RbacMiddleware
{
/**
* 处理请求
* @param \think\Request $request 请求对象
* @param \Closure $next 下一个中间件
* @return Response
* @throws HttpException
*/
public function handle($request, \Closure $next): Response
{
// 获取当前用户ID
$userId = $request->uid ?? 0;

if (!$userId) {
throw new HttpException(401, '用户未登录');
}

// 获取当前访问的控制器和方法
$controller = $request->controller();
$action = $request->action();
$rule = $controller . '/' . $action;

// 检查权限
$rbacService = new RbacService();
if (!$rbacService->checkPermission($userId, $rule)) {
Log::warning('权限验证失败', [
'user_id' => $userId,
'rule' => $rule,
'ip' => $request->ip(),
'url' => $request->url()
]);

throw new HttpException(403, '没有访问权限');
}

return $next($request);
}
}

3. RBAC服务类

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
<?php
namespace app\service;

use app\model\User;
use app\model\Role;
use app\model\Permission;
use think\facade\Cache;
use think\facade\Db;

/**
* RBAC权限服务
*/
class RbacService
{
/**
* 检查用户权限
* @param int $userId 用户ID
* @param string $rule 权限规则
* @return bool
*/
public function checkPermission($userId, $rule)
{
// 超级管理员拥有所有权限
if ($this->isSuperAdmin($userId)) {
return true;
}

// 从缓存获取用户权限列表
$permissions = $this->getUserPermissions($userId);

// 检查精确匹配
if (in_array($rule, $permissions)) {
return true;
}

// 检查通配符匹配
foreach ($permissions as $permission) {
if ($this->matchWildcard($permission, $rule)) {
return true;
}
}

return false;
}

/**
* 获取用户权限列表
* @param int $userId 用户ID
* @return array
*/
public function getUserPermissions($userId)
{
$cacheKey = 'user_permissions_' . $userId;

return Cache::remember($cacheKey, function() use ($userId) {
$permissions = Db::name('permissions')
->alias('p')
->join('role_permissions rp', 'p.id = rp.permission_id')
->join('user_roles ur', 'rp.role_id = ur.role_id')
->join('roles r', 'ur.role_id = r.id')
->where('ur.user_id', $userId)
->where('p.status', 1)
->where('r.status', 1)
->column('p.rule');

return array_unique($permissions);
}, 1800); // 缓存30分钟
}

/**
* 获取用户菜单权限
* @param int $userId 用户ID
* @return array
*/
public function getUserMenus($userId)
{
$cacheKey = 'user_menus_' . $userId;

return Cache::remember($cacheKey, function() use ($userId) {
$menus = Db::name('permissions')
->alias('p')
->join('role_permissions rp', 'p.id = rp.permission_id')
->join('user_roles ur', 'rp.role_id = ur.role_id')
->join('roles r', 'ur.role_id = r.id')
->where('ur.user_id', $userId)
->where('p.status', 1)
->where('p.type', 1) // 菜单类型
->where('r.status', 1)
->field('p.id,p.name,p.rule,p.pid,p.icon,p.sort')
->order('p.sort asc, p.id asc')
->select()
->toArray();

return $this->buildMenuTree($menus);
}, 1800);
}

/**
* 分配角色给用户
* @param int $userId 用户ID
* @param array $roleIds 角色ID数组
* @return bool
*/
public function assignRoles($userId, $roleIds)
{
Db::startTrans();

try {
// 删除原有角色
Db::name('user_roles')->where('user_id', $userId)->delete();

// 分配新角色
if (!empty($roleIds)) {
$data = [];
foreach ($roleIds as $roleId) {
$data[] = [
'user_id' => $userId,
'role_id' => $roleId,
'create_time' => time()
];
}
Db::name('user_roles')->insertAll($data);
}

Db::commit();

// 清除用户权限缓存
$this->clearUserCache($userId);

return true;
} catch (\Exception $e) {
Db::rollback();
return false;
}
}

/**
* 分配权限给角色
* @param int $roleId 角色ID
* @param array $permissionIds 权限ID数组
* @return bool
*/
public function assignPermissions($roleId, $permissionIds)
{
Db::startTrans();

try {
// 删除原有权限
Db::name('role_permissions')->where('role_id', $roleId)->delete();

// 分配新权限
if (!empty($permissionIds)) {
$data = [];
foreach ($permissionIds as $permissionId) {
$data[] = [
'role_id' => $roleId,
'permission_id' => $permissionId,
'create_time' => time()
];
}
Db::name('role_permissions')->insertAll($data);
}

Db::commit();

// 清除相关用户缓存
$this->clearRoleUsersCache($roleId);

return true;
} catch (\Exception $e) {
Db::rollback();
return false;
}
}

/**
* 判断是否为超级管理员
* @param int $userId 用户ID
* @return bool
*/
private function isSuperAdmin($userId)
{
$superAdminIds = config('rbac.super_admin_ids', [1]);
return in_array($userId, $superAdminIds);
}

/**
* 通配符匹配
* @param string $pattern 模式
* @param string $rule 规则
* @return bool
*/
private function matchWildcard($pattern, $rule)
{
// 支持 * 通配符
if (strpos($pattern, '*') !== false) {
$pattern = str_replace('*', '.*', $pattern);
return preg_match('/^' . $pattern . '$/', $rule);
}

return false;
}

/**
* 构建菜单树
* @param array $menus 菜单数组
* @param int $pid 父级ID
* @return array
*/
private function buildMenuTree($menus, $pid = 0)
{
$tree = [];

foreach ($menus as $menu) {
if ($menu['pid'] == $pid) {
$children = $this->buildMenuTree($menus, $menu['id']);
if (!empty($children)) {
$menu['children'] = $children;
}
$tree[] = $menu;
}
}

return $tree;
}

/**
* 清除用户权限缓存
* @param int $userId 用户ID
*/
private function clearUserCache($userId)
{
Cache::delete('user_permissions_' . $userId);
Cache::delete('user_menus_' . $userId);
}

/**
* 清除角色相关用户的缓存
* @param int $roleId 角色ID
*/
private function clearRoleUsersCache($roleId)
{
$userIds = Db::name('user_roles')
->where('role_id', $roleId)
->column('user_id');

foreach ($userIds as $userId) {
$this->clearUserCache($userId);
}
}
}

4. 权限管理控制器

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<?php
namespace app\admin\controller;

use app\service\RbacService;
use app\model\Role;
use app\model\Permission;
use think\facade\Db;
use think\facade\Validate;

/**
* 权限管理控制器
*/
class RbacController
{
protected $rbacService;

public function __construct()
{
$this->rbacService = new RbacService();
}

/**
* 角色列表
* @return \think\response\Json
*/
public function roleList()
{
$page = input('page', 1);
$limit = input('limit', 20);
$keyword = input('keyword', '');

$query = Role::order('sort asc, id desc');

if ($keyword) {
$query->where('name', 'like', '%' . $keyword . '%');
}

$list = $query->paginate([
'list_rows' => $limit,
'page' => $page
]);

return json([
'code' => 200,
'message' => '获取成功',
'data' => $list->items(),
'total' => $list->total()
]);
}

/**
* 创建角色
* @return \think\response\Json
*/
public function createRole()
{
$data = input('post.');

$validate = Validate::rule([
'name' => 'require|max:50|unique:roles',
'description' => 'max:255',
'sort' => 'integer'
]);

if (!$validate->check($data)) {
return json([
'code' => 400,
'message' => $validate->getError()
]);
}

$role = new Role();
$result = $role->save([
'name' => $data['name'],
'description' => $data['description'] ?? '',
'sort' => $data['sort'] ?? 0,
'status' => 1,
'create_time' => time()
]);

if ($result) {
return json([
'code' => 200,
'message' => '创建成功',
'data' => ['id' => $role->id]
]);
} else {
return json([
'code' => 500,
'message' => '创建失败'
]);
}
}

/**
* 分配权限给角色
* @return \think\response\Json
*/
public function assignPermissions()
{
$roleId = input('role_id');
$permissionIds = input('permission_ids', []);

if (!$roleId) {
return json([
'code' => 400,
'message' => '角色ID不能为空'
]);
}

$result = $this->rbacService->assignPermissions($roleId, $permissionIds);

if ($result) {
return json([
'code' => 200,
'message' => '分配成功'
]);
} else {
return json([
'code' => 500,
'message' => '分配失败'
]);
}
}

/**
* 获取权限树
* @return \think\response\Json
*/
public function permissionTree()
{
$permissions = Permission::where('status', 1)
->order('sort asc, id asc')
->select()
->toArray();

$tree = $this->buildPermissionTree($permissions);

return json([
'code' => 200,
'message' => '获取成功',
'data' => $tree
]);
}

/**
* 构建权限树
* @param array $permissions 权限数组
* @param int $pid 父级ID
* @return array
*/
private function buildPermissionTree($permissions, $pid = 0)
{
$tree = [];

foreach ($permissions as $permission) {
if ($permission['pid'] == $pid) {
$children = $this->buildPermissionTree($permissions, $permission['id']);
if (!empty($children)) {
$permission['children'] = $children;
}
$tree[] = $permission;
}
}

return $tree;
}
}

路由配置与中间件使用

1. 路由配置

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
<?php
// route/app.php
use think\facade\Route;

// API路由组 - 需要JWT认证
Route::group('api', function () {
// 用户相关
Route::post('login', 'auth/login');
Route::post('logout', 'auth/logout');
Route::post('refresh', 'auth/refresh');

// 需要权限验证的路由
Route::group(function () {
Route::get('user/profile', 'user/profile');
Route::post('user/update', 'user/update');

// 管理员路由
Route::group('admin', function () {
Route::get('role/list', 'rbac/roleList');
Route::post('role/create', 'rbac/createRole');
Route::post('role/assign', 'rbac/assignPermissions');
Route::get('permission/tree', 'rbac/permissionTree');
})->middleware([\app\middleware\RbacMiddleware::class]);

})->middleware([\app\middleware\JWTAuthMiddleware::class]);

})->prefix('api/');

2. 中间件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// config/middleware.php
return [
// 全局中间件
\think\middleware\CheckRequestCache::class,
\think\middleware\LoadLangPack::class,
\think\middleware\SessionInit::class,

// 别名中间件
'alias' => [
'jwt' => \app\middleware\JWTAuthMiddleware::class,
'rbac' => \app\middleware\RbacMiddleware::class,
],

// 优先级
'priority' => [
\app\middleware\JWTAuthMiddleware::class,
\app\middleware\RbacMiddleware::class,
],
];

最佳实践总结

中间件开发最佳实践

  1. 单一职责原则:每个中间件只负责一个特定的功能
  2. 异常处理:合理处理各种异常情况,提供友好的错误信息
  3. 日志记录:记录关键操作和异常信息,便于问题排查
  4. 性能优化:使用缓存减少数据库查询,提高响应速度

权限控制最佳实践

  1. 权限设计

    • 采用RBAC模型,支持角色继承和权限组合
    • 支持通配符权限,提高配置灵活性
    • 区分菜单权限和操作权限
  2. 安全考虑

    • 实现登录失败次数限制
    • 支持token黑名单机制
    • 记录敏感操作日志
  3. 性能优化

    • 权限信息缓存,减少数据库查询
    • 合理设置缓存过期时间
    • 及时清理过期缓存
  4. 用户体验

    • 支持token自动刷新
    • 提供友好的权限提示信息
    • 支持前端权限控制

通过合理的中间件设计和完善的权限控制系统,可以构建一个安全、高效、易维护的Web应用。在实际项目中,需要根据具体的业务需求调整权限模型和中间件逻辑。

本站由 提供部署服务