插件菜单注册时漏了这一步,后台直接"查无此页"——路由别名与权限节点的暗坑记录

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
站长杂谈 67 浏览 0 回复

上周给后台装了个自己写的插件,前端页面正常,数据接口也通,就他妈后台菜单死活点不进去。F12 一看 404,路由没匹配上。折腾了俩小时才发现是 menu.phproute 字段和实际控制器方法对不上号——我写的是 plugin.foo/log/index,路由注册时手滑写成了 plugin.foo/log/lists。这玩意儿不报错,就是静默消失,比报错还恶心。

后来翻了下 ThinkPHP 插件机制的源码,发现菜单注册和路由解析是两条线走的。菜单只管展示,权限节点管鉴权,路由别名管实际访问。三条线但凡有一条对不上,结果就是:超管能看见菜单但点不开,普通管理员干脆看不见,或者更诡异的——能看见、能点开、但报无权限。三种症状对应三种错法,排查时得挨个对。

说几个我踩实了的细节:

一、route 字段的"隐形前缀"

插件菜单的 route 不是你想写啥写啥。TP 的插件路由默认带 plugin. 前缀,但菜单解析时又会拼一次。我试过直接写全路径 plugin.foo/admin.log/index,结果解析成 plugin.plugin.foo/admin.log/index,双倍的 plugin,双倍的快乐。现在我的土办法是:先在路由文件里 php artisan route:list | grep plugin 把实际注册的路由名复制出来,再往菜单配置里贴,绝不手打。

二、auth_rule 表的 name 字段是"权限身份证"

权限节点声明时,name 必须和菜单 route 保持一致吗?不一定。看你怎么写中间件的鉴权逻辑。我早期图省事,菜单路由是 plugin.foo/log/index,权限节点名写成 foo_log_index,中间件里硬编码映射。结果后来加了个子菜单,忘了补映射,权限校验直接漏过去——不是报错,是漏过去,用户没权限却能访问。现在强制自己用路由名当权限节点名,丑点但省脑子。

三、pid 与 level 的"连环套"

插件安装时往 auth_rule 插数据,pid 指向父级节点的 ID。但如果父节点是另一个插件创建的,或者安装顺序不对,pid 就可能指到空气里。更阴间的是 level 字段,我以为是自动算的,结果有的框架版本要手动写。level 错了,树形菜单渲染直接断层,子菜单挂在根目录下面,和其他插件的菜单搅成一团。现在安装脚本里我必加一段校验:插完数据再查一遍,pid 对应的记录是否存在,level 是否等于父级 level + 1,不对就抛异常回滚,总比装完才发现强。

四、那个被我忽略的 "menu_list" 缓存

最魔幻的一次:菜单配置改了,数据库也更新了,后台刷新十遍还是老样子。最后发现是 menu_list 缓存没清,而且这缓存 key 带用户角色 ID,我清了自己的,测试账号的没清,换号测还以为修好了。现在插件安装/卸载/更新时,强制走一遍 Cache::tag('menu')->clear(),宁可错杀一千。有些老项目没打 tag,就遍历所有可能的 key 前缀去删,土但有效。

五、权限节点的 "半自动同步" 陷阱

我写过一段"智能同步":插件安装时自动扫描控制器里的方法,生成权限节点。听起来很美,实际坑死——控制器里如果有 protected 方法、trait 引入的方法、或者父类的 initialize,全被扫进去了。更坑的是 RESTful 路由,一个 resource 注册出来七八个方法,菜单上只展示两个,权限节点却生了一堆,超管的权限配置页拉不到底。后来改成白名单模式,只在控制器里加 @authNode 注解才同步,麻烦但干净。

现在我的插件安装流程是:路由注册 → 菜单插入 → 权限节点插入 → 缓存清理 → 全角色权限刷新,五步全走脚本,不手点。菜单配置单独抽一个 menu.json,安装时解析入库,卸载时按插件标识批量删。之前硬编码在 PHP 数组里,卸载时漏删干净,数据库里一堆孤儿节点,越积越多。

有个问题还没想好:插件升级时,如果用户手动改过菜单排序或权限分配,自动同步会不会覆盖掉?目前做法是升级只增删,不改已有记录的 pidsortstatus,但新增节点的默认排序可能插错位置。各位是怎么处理插件升级时的菜单/权限迁移的?

评论0
回复 · 0
还没有回复
微信客服 微信客服