ThinkPHP权限控制需在请求进入控制器前校验具体动作权限码(如article:delete),权限码应在路由定义时显式绑定或按规则映射,存入auth_rule.rule_code并加唯一索引;中间件须基于Redis角色粒度缓存权限集合,O(1)判断,且权限变更时同步清理对应缓存;API场景禁用依赖Session的Auth::check(),应从JWT payload读取权限或无状态查库;按钮显隐仅为前端体验,后端必须校验接口权限并字段级过滤响应数据;权限缓存清理须与后台操作强绑定。

ThinkPHP 实现权限控制接口,核心不是加个中间件就完事——接口权限必须落到具体动作(如 article:delete),且校验必须在请求进入控制器前完成,否则容易被绕过或漏判。
权限码怎么定义才不会和路由、控制器脱节
权限码不能靠 $this->request->action() 拼接生成,它返回的是方法名(如 delete),而真实权限需求是「文章模块的删除操作」,需带上下文。直接用会导致同一方法在不同路由下权限混乱。
- 推荐显式声明:在路由定义时用注释或扩展属性绑定权限码,例如
Route::post('admin/article/delete', 'ArticleController@delete')->option(['permission' => 'article:delete']); - 或统一映射规则:把
admin/article/delete路由名转为article:delete,去掉前缀、统一用冒号分隔,避免大小写/斜杠差异 - 数据库中
auth_rule.rule_code字段必须存这个字符串,且加唯一索引,防止重复或拼写错误 - 别用中文、空格、特殊符号——
rule_code是程序判断依据,不是给用户看的
中间件里怎么查权限才不拖慢接口
每次请求都查三张表(user_role → role_permission → auth_rule)会明显拉高延迟,尤其并发上来后 DB 成瓶颈。
- 缓存必须按角色粒度切分,key 用
'role_permissions_' . $roleId,不能用全局 key;否则 A 角色删了权限,B 角色缓存未过期仍能访问 - 首次查询走 DB,组装成
Set结构存 Redis(如SMEMBERS),后续直接SISMEMBER判断,O(1) 时间复杂度 - 权限变更时(如后台分配/回收),必须同步清理对应
role_permissions_*缓存,否则新权限永不生效 - 禁止用
static $cache = []这类进程内缓存——Swoole 下 worker 间不共享,极易出现部分请求误判
为什么 Auth::check() 在 API 接口里总失效
因为 Auth::check() 默认依赖 Session,而 API 多用 Token/JWT 认证,Session 根本没初始化,调用结果恒为 false,跟权限配置无关。
立即学习“PHP免费学习笔记(深入)”;
- 不要在 API 中间件里直接调
Auth::check($uid, $node),它底层仍查 Session 或预设的静态数组 - JWT 场景下,权限应直接签发进 payload:
"permissions": ["article:delete", "user:export"],中间件里用in_array('article:delete', $token->permissions)判断 - 若必须查库(如权限动态更新频繁),则改用无状态方式:从 token 解出
user_id,再查auth_role_rule关联数据,全程不碰 Session -
$node参数必须和数据库auth_rule.rule_code完全一致——article/delete和article:delete是两条不同规则
按钮级权限和字段级权限怎么一起控
前端 data-permission="article:delete" 控按钮显隐只是体验优化,后端接口校验是底线;但字段级(如工资字段仅 HR 可见)必须在数据组装阶段过滤,不能靠模板 if。
- 接口返回前做字段裁剪:
array_diff_key($data, ['salary_real' => '', 'id_card' => '']),确保敏感键根本不出响应体 - 别在控制器里写一堆
if ($this->checkPermission('hr:salary')) { $data['salary_real'] = ... }——逻辑分散,难维护 - 建议封装成服务类:
FieldFilterService::apply($data, $userId, $resourceType),复用逻辑、集中管控 - 注意:字段权限和操作权限是两套体系,不能混用同一个
rule_code,比如article:delete不代表能看article.content_raw
最常被忽略的一点:权限缓存清理必须和业务操作强绑定。后台点「分配权限」按钮,代码里没调 cache('role_permissions_' . $roleId, null),那前端刷新一百次,接口照样 403——这不是 Bug,是设计缺失。



















