从 PHP 到 AI + Golang,程序员自救转型手记(五十二):管理员权限检查中间件,AI 随意放行预闯大祸

📅 2026/8/5 20:11:29
从 PHP 到 AI + Golang,程序员自救转型手记(五十二):管理员权限检查中间件,AI 随意放行预闯大祸
这是一个系列 Blog作者将以一个 PHP 全栈工程师的身份利用 AI 工具claude code、codex、deepseek、豆包等从零开始学习 golang 语言并最终完成 ai-go-admingithub | gitee开源项目的制作全程记录分享。在上一期我们进行了 “管理员和角色组的关联”本期将完成管理员权限检查中间件管理员权限检查中间件之前我们已经完成了RBAC权限模型的开发接下来建立一个权限检查中间件对管理员的每个操作进行检查。我将此中间件取名为AdminPermission权限和已有的AdminAuth认证对应。和BuildAdmin不同本系统认证和权限检查都是在中间件完成中间件的使用方式// 必须登录group.GET(/init,middleware.AdminAuth(),h.Init)// 可选登录若传递了正确的 token中间件将在请求上下文注入登录管理员信息没传递 token 也会放行group.POST(/upload,middleware.AdminAuthOptional(),h.Upload)// 必须登录同时检查权限// 权限检查中间件就是本文的目标它必须和 middleware.AdminAuth() 中间件配套使用毕竟没登录咋验权呢group.POST(/list,middleware.AdminAuth(),middleware.AdminPermission(),h.List)AI 做出来的首版验权中间件在管理员未登录时直接放行并表示由 AdminAuth 拦截实际上 AdminPermission 比 AdminAuth 后执行放行是绝对错误的未匹配到权限规则时居然也会放行等相当于 AI 把放行当做默认操作如果没有人工 review 或测试这将是一个巨大的安全漏洞。接下来直接让 AI 给规划一下我需要一个admin_permission.goAdminPermission中间件需要使用它对管理员的每次请求进行验权你觉得如何写比较合适要求和上下文如下权限节点种子数据在cmd\migrate\migrations\000003_seed.up.sql里边有权限规则表对应的模型在internal\model\admin.go中的AdminRule模型你可以先读取它们的字段类型定义从gin请求上下文读取当前匹配到的路由从中解析出权限节点的 name然后调用permission.Check验权对于List和Get请求可以检查路由路径/read权限节点AI 完成了种子数据的读取并完成了初版实现人工review在里边发现了一下问题错误的跳过验权的路由AI 先定义了一个permissionSkipPathsmap里边存储了需要跳过验权的路由PSAI 将/admin/auth/admin_log/get/:pk这种路径称为路由模式因为它们是匹配成功的已注册路由带原样的/get/:pk字样而不是用户请求时的/get/1这种// permissionSkipPaths AdminPermission 跳过验权的路由模式varpermissionSkipPathsmap[string]struct{}{/admin/init:{},/admin/clear-cache:{},/admin/logout:{},/admin/auth/admin_log/list:{},/admin/auth/admin_log/get/:pk:{},}然后在验权中间件里边进行跳过// 跳过列表中的路由不验权if_,skip:permissionSkipPaths[c.FullPath()];skip{c.Next()return}这种跳过方式是错误的因为根据我们的设计目标不验权的直接不使用 AdminPermission 中间件就行了应该直接删除这些多余的逻辑。严重问题意外的放行如下代码提取片段中间件内有多处放行admin:GetAdmin(c)ifadminnil{// 未登录由 AdminAuth 拦截 401放行c.Next()return}action,nodeSuffix:extractAction(fullPath)ifnodeSuffix{// 无法识别动作放行c.Next()return}首先未登录的直接放行就是严重的错误应该直接拦截并提示未登录。其次AI写了一个从fullPath末尾提取当前动作的方法如果提取失败也放行这与我们的设计不符唯二放行规则应该是超管和存在匹配的节点。改造权限规则管理器的 Check 方法我们设计的权限节点并不会区分list和get请求我本是想统一检查read权限节点。AI 写出来的版本中规中矩就是先检查list节点没有就继续检查read节点但是这会查库两次所以我们重新规划一下。我们从permission.Check方法入手让它支持在一次查询中验证两个节点就行了如下中间件内无需超管短路permission.Check里边已经有了permission.Check的ruleName改为ruleNames []string额外增加一个op参数值是OR或AND默认然后将name字段的数据库查询操作符号改为INopOR只要有结果就返回trueopAND时需要全部有结果才返回true检查路径末尾是list的请求时同时向permission.Check传递read的Check pathlist的在前op设为OR其余权限节点检查全部用AND对Check方法的改造总体很简单人工review只找到两个细节问题一、应该对op统一转小写后再进行检查二、操作符号限定在or和and之间// 原来的ifop!{opand}// 改为ifop!or{opand}中间件逻辑重新整理现在可以重新梳理整个中间件的逻辑核心其实非常简单被 AI 搞复杂了就一件事而已根据路由模式构建权限节点的 name然后直接传递给permission.Check就行了。在AI之前的规划中我们得知原来我们可以获取到当前请求的路由模式匹配成功的已注册路由带原样的/get/:pk字样而不是用户请求时的/get/1这种。如果是用户请求的URL要提取权限节点确实有点麻烦因为参数数量是不固定的但是现在从固定的路由模式提取节点名称可就太简单了示例路由模式/admin/auth/admin-log/get/:pk去掉开头的/admin/得到auth/admin-log/get/:pk以/切割为[auth, admin-log, get, :pk]从第一个含有:符号的元素开始全部丢弃剩下的用/再拼回string就是权限节点的name了路由模式权限节点 name/admin/auth/admin-log/get/:pkauth/admin-log/get/admin/auth/test/create/:pk/infoauth/test/create/admin/auth/test/updateauth/test/update节点 name 是管理员自己在菜单规则管理中定义的只要遵循以上规则节点自然就能匹配到匹配不到也没关系程序不会放行又想起之前 AI 认为可以放行太坑了管理员再自行修改就行了。