动态菜单权限必须基于auth_rule表中type='menu'的记录实现,通过auth::getrulelist()获取用户实际权限菜单,并在后端组装树形结构、校验路由名与权限白名单匹配,禁止前端过滤。

动态菜单权限在 ThinkPHP 中不是靠模板硬写或前端拼接实现的,而是必须让每个菜单项对应一条 auth_rule 记录,并通过 Auth::getRuleList() 获取当前用户实际拥有的、type = 'menu' 的规则集合。否则菜单和权限永远是两张皮。
菜单数据怎么从 auth_rule 表里查出来才对
ThinkPHP 6 的 RBAC 不自带菜单表,auth_rule 就是菜单源——前提是字段用对、查询逻辑不绕弯。
-
type字段必须显式设为'menu'(不是默认的'url'),否则Auth::getRuleList()不会把它当菜单处理 -
name字段存唯一英文路径标识,如'admin.user.list',不能是中文、空格或带查询参数的完整 URL -
title存显示名(如“用户列表”),icon和sort需自行扩展字段,原生无支持 - 查的时候加
where('type', 'menu')->where('status', 1),漏掉status过滤会导致禁用菜单仍显示
为什么 getRuleList() 返回的菜单没层级,还全是平铺的
Auth::getRuleList() 原生只返回一维数组,它不管父子关系;树形结构得你用 PHP 组装,但别写递归函数查 N+1 次数据库。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 一次性查出全部
type = 'menu'的记录:Db::name('auth_rule')->where('type', 'menu')->select() - 用引用方式组装树,避免递归调用和重复遍历:
$map[$item['id']] = $item+$map[$item['pid']]['children'][] = &$map[$item['id']] - 父级菜单的
name要能被子级前缀匹配,比如父项是'admin.user',子项是'admin.user.list',这样权限校验才能按树展开 - 别把组装逻辑放在模板里,应在控制器或服务类中完成,再传给视图
菜单渲染时怎么判断当前项该高亮,又不越权
高亮不是靠 $_SERVER['REQUEST_URI'] 字符串匹配,也不是单纯看路由是否相等,而是结合路由名 + 权限白名单双重判断。
- 用
request()->rule()->getName()拿当前路由名(如'admin/user/list'),再跟菜单的name字段做前缀或精确匹配 - 匹配前必须确认该菜单项已在用户权限集合中——即
in_array($menu['name'], $allowedMenuNames),否则可能高亮一个用户根本没权限访问的菜单 - 不要在模板里逐个调用
Auth::check($menu['name'])做判断,开销太大;应提前把用户所有可访问的name构建成array_flip()映射表 - 如果菜单
url字段存的是相对路径(如'/admin/user/edit'),而路由名是'admin/user/edit',注意去除开头斜杠再比对
缓存键为什么总导致菜单不更新
缓存失效不是靠时间过期,而是靠变更感知——Auth::getRuleList() 默认缓存键只含角色 ID,多个用户共用同一角色但菜单配置不同,就会互相污染。
- 必须重写
getRuleList()方法,把缓存键从'auth_rule_list_' . $roleId改成'auth_rule_list_user_' . $userId - 角色或菜单变更后,立刻执行
Cache::delete('auth_rule_list_user_' . $uid),别等缓存自动过期 - 如果用了 Redis,缓存 TTL 设 300 秒足够;设太长(如 3600)会导致菜单改了用户半天看不到
- 菜单表本身有更新时(比如改了
sort或icon),也要同步更新缓存键里的版本信号,例如拼上max_update_time
最常被忽略的一点:菜单数据必须在后端完成权限过滤,绝不能把全量菜单传给前端再用 JS 隐藏——那只是障眼法,接口依然可被直接调用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










