权限节点必须用rule_name(如admin.user.delete)作唯一标识,不可用中文或ID;中间件需通过request()->rule()->getName()获取路由名并匹配缓存中的role_id维度Set集合,Auth::check()等方法不校验具体节点权限。

权限节点必须用 rule_name,别用中文或 ID 做判断依据
RBAC 权限控制翻车的第一现场,就是把权限节点存成「用户管理」或 id=5 这种形式。中间件一跑起来就懵:它不知道「用户管理」对应哪个路由,更没法和 admin/user/delete 匹配上。
正确做法是统一用小写+点号分隔的标识符,比如:
-
admin.user.list对应 GET/admin/user -
admin.user.delete对应 POST/admin/user/delete -
admin.role.edit对应 PUT/admin/role/:id
这些 rule_name 必须和路由定义严格对齐——不是靠猜,而是靠 request()->rule()->getName() 拿到原始规则名后,做一次格式转换(如把 admin/user/edit 转成 admin.user.edit)再查权限。
中间件里别查全量权限表,要用 role_id + 缓存键隔离
常见错误是中间件一进来就 AuthRoleRule::where('role_id', $roleId)->select(),每次请求都查库,性能差还容易被绕过。更糟的是缓存全量节点不带角色维度,A 角色删了权限,B 角色的缓存还在生效。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 缓存 key 必须包含
role_id,例如auth_rule_set_12 - 缓存内容用 Set 结构(Redis 的
SMEMBERS),只存rule_name字符串集合 - 没命中缓存时,才联合查询
auth_role_rule和auth_rule,生成并写入缓存,TTL 设为 30 分钟 - 权限变更后,主动执行
cache('auth_rule_set_'.$roleId, null)清空对应缓存
Auth::check() 和 $this->auth->hasRole() 根本不校验节点权限
这两个方法只回答「你登录了吗?」和「你属于管理员角色吗?」,完全不碰 admin.user.delete 这类具体操作。所以你会看到:登录成功、角色也对,但点删除按钮后端照样执行——因为中间件根本没挂到那个路由上。
排查重点:
- 确认中间件已注册进
app/middleware.php的全局或分组中间件数组 - 检查中间件顺序是否在
SessionInit之后(否则拿不到 session) - 看路由是否用了
allowCrossDomain或其它跳过中间件的配置 - 打印
request()->rule()->getName(),确认它返回的是你预设的规则名,而不是__thinkphp_route__这类兜底值
密码不能明文存,但 think\facade\Hash 不是唯一解
ThinkPHP 6 默认不带密码哈希工具,think\facade\Hash 是可选扩展,不是核心组件。直接 password_hash($pwd, PASSWORD_DEFAULT) 更轻量、更可控。
在模型的 save 或控制器的 create 之前处理:
$data['password'] = password_hash($request->post('password'), PASSWORD_DEFAULT);
Admin::create($data);
验证时用 password_verify($input, $dbHash),别自己写 MD5 或 sha1 ——这些算法早就不安全,且无法兼容未来升级的哈希策略。
真正容易被忽略的点是:密码字段的数据库长度必须 ≥ 255,否则 PASSWORD_DEFAULT 生成的 bcrypt 字符串会被截断,导致永远验不过。



















