插件菜单注册时权限节点漏写了一个字母,导致半个后台功能"幽灵化"了半小时

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

昨天给后台加一个新插件,菜单倒是正常出来了,点击却直接 403。排查了二十分钟才发现,我在 auth_rule 里注册的节点名是 plugin.data.export,但插件配置文件里写的是 plugin.data.exprot——就这一个字母错位,权限校验死活过不了,菜单却照样渲染,因为菜单注册和权限检查是两条独立逻辑。

这事让我重新理了理后台菜单那套流程,发现几个容易手滑的地方,记下来给自己提个醒。

一、菜单注册和权限节点其实是"先婚后恋爱"

很多框架(包括我用的这套基于 ThinkPHP 的二次开发平台)把菜单展示和权限校验拆成了两个步骤:

1. 菜单注册:告诉系统"后台左边栏要出现什么"
2. 权限节点声明:告诉系统"谁有资格点这个"

这两步数据源头经常不一样。菜单可能读插件目录下的 menu.php,权限节点却读 auth_rule 表或者 Permission 注解。如果两边字符串对不上,就会出现"看得见点不动"或者"看不见但能直接输 URL 访问"的诡异情况。

我现在的做法是:插件安装时统一走一个 InstallService,菜单插入和权限节点写入放在同一个事务里,且节点名从同一个常量里取,禁止手写字符串。

二、权限节点命名空间别和路由前缀打架

之前还踩过一个坑:路由前缀配的是 /admin/plugin-data/,权限节点我图省事写成 admin.plugin.data.xxx。结果中间件里做鉴权时,是把当前路由按规则转成节点名去匹配的,转换规则是"去掉前缀斜杠,斜杠变点"。

看起来一样?不对。如果路由带参数,比如 /admin/plugin-data/export/{type},转换逻辑如果没处理好,会变成 admin.plugin.data.export.{type},和注册的 admin.plugin.data.export 对不上。

我的解决办法是在中间件里做节点匹配时,统一把路由里的参数占位符抹掉再比对,而不是要求每个带参数路由都注册一遍通配节点。

三、那个让我愣了半分钟的"缓存幽灵菜单"

最离谱的一次,我明明把插件卸载了,左边栏还挂着那个菜单。清浏览器缓存、换无痕模式都没用,最后发现是框架把菜单树整棵缓存到了 Redis,key 是 admin:menu_tree:{role_id},但插件卸载时只清除了数据库记录,没触发缓存刷新。

现在我的卸载流程里强制加了一步:Cache::tag('admin.menu')->clear(),虽然粗暴,但总比让用户看到已卸载插件的僵尸菜单强。

四、一个小习惯:节点名全小写,禁止驼峰

团队里有人习惯写成 plugin.DataManage,Linux 环境下文件系统倒是没问题,但权限节点做字符串比对时,MySQL 默认不区分大小写,代码里却可能区分,跨环境容易出幺蛾子。我现在直接代码规范里写明:权限节点必须全小写,单词用点分隔,禁止驼峰和下划线混用。

——

说到底,后台菜单这套东西不复杂,就是"注册-展示-鉴权"三个环节,但每个环节数据源不一致、缓存层不一致、大小写规则不一致,组合起来坑就多了。你们插件菜单注册这块有没有遇到过更离谱的?比如菜单排序权重冲突、或者父子级权限继承失效之类的?

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