菜单注册时我把 `auth_rule` 写成了 `auth_rules`,结果超级管理员也看不见那个按钮

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

上周给后台加一个新插件,功能本身半天就写完了,结果在菜单注册上卡了将近两个小时。问题小得可笑,但排查过程让我把权限节点的注册链路重新理了一遍,记录一下。

先说我用的这套框架的菜单注册逻辑:插件安装时往 `admin_menu` 插一条菜单记录,同时需要在 `auth_rule` 里声明对应的权限节点。后台渲染侧边栏时,会拿当前角色的权限去和菜单绑定的 `auth_rule` 名称做匹配,匹配不上就不显示。

我当时的代码大概长这样:

```php // 插件安装时的菜单注册 Menu::add([ 'title' => '数据归档', 'icon' => 'fa fa-archive', 'href' => 'plugin/archive/index', 'auth_rule' => 'plugin/archive/index', // 这里 ]); ```

然后在权限节点声明文件里写了:

```php return [ 'plugin/archive/index' => '归档列表', 'plugin/archive/run' => '执行归档', ]; ```

安装完插件,清缓存,登录超管账号——菜单没出来。F12 看接口返回的菜单树,确实少了这一条。但数据库里 `admin_menu` 明明有记录,状态也是正常。

我第一反应是缓存没清干净,Redis 里把 `admin:menu:*` 的 key 删了个遍,没用。又怀疑是 `href` 格式不对,改成全路径、加斜杠、减斜杠,折腾好几轮。

最后是在权限管理页面手动添加节点时发现的。我输入 `plugin/archive/index`,系统提示"节点已存在"。但列表里搜不到。去数据库一看,`auth_rules` 表里有一条 `plugin/archive/index`,而我刚才手动输入的查询条件是 `auth_rule`——框架的模型里把表名写错了?不对,再仔细看,是我插件安装脚本里的问题。

翻回安装脚本,找到这段:

```php Db::name('auth_rules')->insert([ 'name' => 'plugin/archive/index', 'title' => '归档列表', ]); ```

`auth_rules`,多了个 s。框架自带的权限校验走的是 `auth_rule`,我插件安装时却写进了 `auth_rules`。两张表结构一模一样,数据正常插入了,菜单注册时去 `auth_rule` 查匹配,当然查不到。超管账号虽然理论上不受权限限制,但这套系统的菜单渲染逻辑是统一的:先查菜单,再过滤,没有对应节点直接丢弃,不走"超管例外"分支。

改回 `auth_rule`,卸载重装,菜单正常出现。整个过程最大的教训不是拼写错误本身,而是这种"影子表"问题太难排查——数据看起来是对的,流程看起来也走通了,就是结果不对。

现在我在插件安装脚本里加了个强制检查:

```php $tables = Db::getTables(); if (!in_array('auth_rule', $tables)) { throw new \Exception('权限表异常,请检查表名'); } ```

另外把权限节点声明从安装脚本里拆出来,单独放一个 `auth.php`,安装时统一读取,不再手写表名。虽然多了个文件,但至少不会再出现"表对不上"的情况。

还有个细节:有些框架的菜单注册支持 `auth_rule` 留空,表示无需权限即可显示。我一开始想过干脆留空绕过这个问题,但后来发现这样会导致所有角色都看见这个菜单,包括那些本该被隔离的测试账号。权限节点这步省不得,哪怕当前只有超管用,也得先把坑占上。

你们插件菜单注册时遇到过什么离谱的拼写错误?我这次算是把"单复数"这个坑踩实了。

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