多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

gin-vue-admin权限配置全流程:从Casbin模型到部署文件权限

gin-vue-admin权限配置全流程:从Casbin模型到部署文件权限 上个月帮客户做一个基于gin-vue-admin圈里常说的GVA的中后台项目需求本身不复杂内部运营和客服两拨人登录之后看到的菜单、能点的按钮、能调的接口必须完全隔离开。我在GVA上把角色、菜单、API、用户这一整套权限链路从头到尾配了一遍中途踩了不少坑也把权限验证的完整链路翻了个底朝天。这篇文章就把这次完整走通的流程写下来。不管你是第一次接触GVA还是已经在用但一直被权限问题折磨这篇内容应该都能给你一个从原理到落地的全貌。先打个预防针GVA的权限设计并不复杂但它是一个贯穿数据库、后端中间件、前端路由、甚至服务器文件系统的完整链路。很多人觉得权限管理就是给用户选几个角色真到上线时才发现菜单可见但接口全403、改了角色不生效、上传目录报错——这些问题全都不是孤立的小bug而是对权限链条缺乏整体认知。下面我从模型说起一步步把这套流程讲透。1. 先说清楚GVA权限模型用户、角色、菜单、API是怎么咬合在一起的1.1 为什么GVA选择Casbin而不是自己写一堆if判断接触GVA的第一天大部分人会看到一个很明显的设计业务接口上几乎没有当前用户是不是管理员这种判断代码。换成我们自己写后台通常是在每个Controller开头来一句 if user.Role ! admin { return error }权限逻辑散落在各处后期根本维护不动。GVA把这部分抽象成了Casbin。Casbin是一个通用的权限控制库核心思路就两条策略policy和分组grouping。策略是谁(sub)对哪个资源(obj)能做什么操作(act)分组是哪个用户属于哪个角色。在GVA里资源就是后端接口的路径请求方法谁就是角色ID而不是具体某个用户。这种设计有一个非常实在的好处新增一个接口要给谁用不用动代码。你在API管理里注册好接口路径把这条记录绑定给某个角色casbin_rule表里就会多一条策略后端Casbin中间件在下一次请求时自动生效。业务开发人员完全不需要关心某个用户的角色判断运营同学也可以在管理后台完成一条权限变更流程这才是中后台项目该有的样子。还有一个细节值得注意GVA的用户表可以关联多个角色所以Casbin里走的是g规则——把用户ID映射到多个角色ID然后再用角色ID去匹配前面的p规则。权限的最终结果自动取并集也就是说只要某一个角色拥有某个API权限用户就能访问。多角色并集这个特性后面做数据隔离的时候特别容易踩到先记住它。1.2 核心表结构与关联关系sys_user、sys_authority、sys_menu、sys_api、casbin_rule权限这块最容易搞混的就是那几张sys开头的表。我把它们拆开来说你对照自己数据库里的表结构看会清晰很多sys_user用户表存账号、密码、基本信息以及一个authorityId主角色ID。sys_authority角色表GVA里把角色叫权限组其实更准确一个角色可以绑多个菜单和多个API。用户还能关联多个角色中间表一般是sys_user_authority。sys_menu菜单表同时也是按钮权限表。type字段区分目录、菜单、按钮parentId构建父子关系。前端路由渲染靠的就是这棵树按钮权限标识也在这棵树上。sys_api接口表每一行就是一个后端接口的path method比如 /api/user/info 和 GET。这是权限校验的最小单位。casbin_ruleCasbin的策略表真正干活的规则数据。p类型记录{角色ID, 路径, 方法}g类型记录{用户ID, 角色ID}。我用表格把这五张表的关系梳理一遍表作用关联方式sys_user用户账号通过authorityId关联主角色通过sys_user_authority关联多角色sys_authority角色/权限组通过角色ID关联菜单、API、用户sys_menu菜单按钮权限通过角色-菜单中间表关联角色sys_api后端API登记通过Casbin规则绑定角色casbin_rule权限策略落地存{角色ID, path, method}和{用户ID, 角色ID}整条数据流是用户登录后拿到自己的角色集合前端拉取这些角色拥有的菜单树生成侧边栏和路由用户点某个按钮或调某个接口时前端用v-auth判断按钮权限后端用JWT确认身份后用Casbin判断接口权限。菜单权限管你当前能看到什么API权限管你能调用什么两层各自独立。我见过不少同事上来就问我给他分配了菜单怎么接口还是403本质就是没搞清楚这两层权限是独立配置的。菜单树分配给角色只是让前端能渲染出页面页面里的每个数据请求后端的Casbin中间件还要拿角色ID、请求路径、请求方法去casbin_rule里做一次完整匹配。你只配了菜单API一条都没绑那结果必然是页面能看到、数据全进不来。2. 跑起来之后的第一件事梳理默认数据和权限初始化状态2.1 初始化数据库时自动生成的默认角色和菜单数据第一次把GVA项目clone下来初始化数据库后直接登录admin你会发现系统里已经带了一套完整的角色、菜单、API数据。默认有超级管理员角色和普通用户角色印象中ID是888和9528这种风格不同版本可能有差异以你初始化后sys_authority表里的数据为准。这套默认数据非常值得花半天时间认真过一遍重点不是看菜单长什么样而是看每个节点的type到底是什么哪些是目录、哪些是菜单、哪些是按钮。我建议重点看几个典型按钮节点比如用户管理下的新增用户删除用户它们在菜单树里也是独立节点type是按钮name是一个权限标识比如 user:add。前端v-auth指令匹配的就是这个name字段。这也是GVA权限设计里我觉得最妙的地方按钮不是前端写死的装饰而是后端菜单树上的一个节点。运营人员在管理后台就能控制一个按钮的显隐不需要前端发版。理解了这一点你再看前端那些 v-auth 指令就不会觉得它们神秘了。2.2 配置文件里与权限相关的开关jwt、casbin、authority权限链路牵扯到几个配置项分布在config.yaml里。最核心的是这么几段jwt段signing-key是token签名密钥expires-time是有效期。上线前一定要改成自己的随机字符串别用默认值。改密钥会导致线上下发的所有token失效这个动作最好安排在版本发布窗口里做。casbin段GVA把Casbin的模型文件路径放在配置文件里通常是rbac_model.conf之类。模型文件定义了匹配规则比如精确匹配还是路径通配这个文件原则上上线后就不要动了不同版本之间的模型语法差异很大乱改很容易把整个权限系统弄瘫。system段router前缀比如 /api它会影响API管理里登记的路径。如果项目部署到子路径这一块也要一起调。有一个值得提前提醒的点config.yaml里的数据库密码和JWT密钥都是敏感信息本地开发环境无所谓生产服务器上一定要控制文件权限。后面第五章我会专门讲文件系统权限这里先立个flag。2.3 验证默认超级管理员权限范围登录admin之后你会发现所有接口都能通。这不是巧合而是初始化数据里把全部API都分配给超级管理员角色了。你去casbin_rule表里看会看到一大堆p规则p的sub列都是超级管理员角色ID。理解这一点很重要因为当你新建一个角色时它默认不绑定任何菜单和API。用新角色登录后前端菜单接口返回的是空数组侧边栏一片空白这是正常现象而不是配置出错了。权限模型默认是最小权限你需要手动把资源一样一样授权给新角色。我建议每次新建角色后用这个新角色单独开一个无痕窗口登录做一次完整的功能验收。不要用admin账号去验证业务页面因为admin的权限太大了它能走通不代表你的角色配置正确。这一步虽然基础但我见过太多团队在这里翻车开发用admin测完说没问题上线后运营新号一登录全是空白和403。3. 手把手走一遍权限配置全流程从新建角色到用户落位3.1 第一步建角色并配置菜单/按钮权限以一个运营专员角色为例走一遍新建流程。进入角色管理新增角色然后进入菜单/按钮权限分配。这里有一个经验分配菜单时如果勾选了父节点最好把子节点一并勾选否则前端渲染菜单树时可能出现父节点下没有任何子节点、整个节点显示异常的情况。菜单树是递归渲染的前端拿到的就是后端返回的树状数据。如果一个父目录下面没有任何菜单节点前端组件可能渲染出一个空的折叠箭头点开什么都没有。这类问题排查起来特别难受因为数据库数据看起来没错但前端表现就是不对。按钮权限要单独勾比如运营角色需要新建内容就勾上新建按钮节点。按钮节点的name是权限标识前端v-auth指令读的是当前用户所有角色的权限标识集合只要有一个角色拥有按钮就显示。所以一个用户挂了多个角色时按钮权限也是取并集的跟API权限行为一致。3.2 第二步注册API并绑定到角色菜单配好只是第一步后面的API绑定才是让功能真正跑起来的关键。以内容管理功能举例后端已经写好了路由 /api/content/list但初始化数据里没有这条记录你必须在API管理页面里新增它path填 /api/content/listmethod填 GET然后把这个API分配给运营专员角色。这时casbin_rule表里就多了一条p规则{运营专员角色ID, /api/content/list, GET}。从这一瞬间开始运营专员的请求才会被Casbin中间件放行。这里有一个最常见的403来源path和method不匹配。后端路由如果是 /api/content/detail/:id你在API管理里登记成 /api/content/detail那Casbin匹配大概率会失败除非模型文件启用了路径模式匹配。我的建议是登记API时直接对照后端路由源码来填不要凭记忆手打。尤其是带冒号参数的路径每个字符都要严格一致。另外说一个容易忽略的点Casbin规则里method是判断因素之一前端请求用POST你登记的是GET哪怕路径一样也会403。调试时不要只盯路径把浏览器Network面板里的Request Method也一起对比。3.3 第三步新建用户并分配角色角色和API都配好之后就该落用户了。用户管理里新建账号填好账号密码分配角色时选择运营专员。GVA支持给一个用户挂多个角色权限取并集。多角色并集这个特性在实际使用中要特别小心。如果某个角色被分配了比较高的权限而用户的另一个角色是低权限那个低权限角色并不能起到限制作用用户最终拿到的是所有角色的并集。也就是说你想用加一个只读角色去约束一个管理员用户这种思路大概率行不通。数据权限层面不同角色往往有自己的数据范围我记得不同版本的处理逻辑有差异上线前一定要在自己部署的版本上实测一遍。创建用户后的另一个关键动作是让用户重新登录一次。因为JWT里携带的是用户登录那一刻的角色集合如果用户已经在别处登录了你这边给他改角色他那边不退出重登是不会生效的。这个问题我排过很多次每次都浪费好几分钟现在写下来给大家提个醒。3.4 第四步前端菜单和按钮的权限衔接用户登录后前端会请求后端菜单接口拿到当前用户的菜单树然后动态注册路由并渲染侧边栏。GVA前端的动态路由是经典做法菜单表的component字段填的是页面组件的路径字符串比如 views/content/index.vue前端拿到后动态import。所以你新增一个业务页面时菜单表里的component路径必须和views目录下真实的文件位置一一对应。按钮权限的衔接靠的是自定义指令v-auth。在按钮上写 v-authcontent:add当按钮权限标识不在当前用户集合里时这个DOM节点会被直接移除。注意是移除而不是禁用所以前端调试时看不到按钮第一反应应该是去菜单管理里看看角色有没有勾选对应的按钮节点权限标识字符串有没有写对。还有一个容易踩的坑动态路由注册失败。如果菜单表的component路径写错了或者页面文件不存在前端会白屏或者跳到404。排查时看浏览器控制台有没有动态import失败的报错有的话往往是component字段写错而不是权限配置问题。这个坑经常被误判成权限问题白白排查很久。4. 权限验证链路拆解一个请求进来后经过了哪些关卡4.1 JWT验身份token里藏着什么登录成功后GVA后端会签发一个JWT。这个token里最主要的载荷就是用户ID和角色ID集合因为后面Casbin中间件需要从token里拿角色ID去做策略匹配。前端每次请求都会在请求头带上Authorization: Bearer xxx后端JWT中间件先校验签名和过期时间。这里有一个绕不开的设计改角色后需要重新登录。因为token里的角色信息是签发时固化进去的后端JWT中间件默认不会再去数据库里查一遍用户当前的角色列表。如果你改了用户的角色却发现权限没变化十有八九是token没刷新。尤其是用接口调试工具测试时一直用同一个旧token怎么改数据库都没用。JWT的过期时间也很关键。权限变更场景下如果过期时间设成7天那用户最坏情况下要7天后才拿到新角色权限这对权限敏感项目是不可接受的。建议过期时间不要超过8小时敏感后台甚至可以设30分钟。真要有长会话需求GVA也有刷新token的思路但那是另一个话题这里按下不表。4.2 Casbin验权限策略规则是怎么匹配的请求过了JWT中间件就轮到Casbin中间件了。它会构造一个三元组{角色ID, 请求路径, 请求方法}然后去casbin_rule表里查有没有匹配的p规则。有就放行没有就返回403。理解这个匹配过程对解决403问题至关重要。我给你的排查顺序是第一步API管理里有没有这条记录没有就新增注意path和method与后端路由完全一致。第二步这条API分配给你的目标角色了吗没有就分配。第三步去casbin_rule表查实际写入的规则看sub对象是不是你期望的角色ID。第四步用浏览器Network面板看实际请求的path和method与规则逐字符对比。这个排查链路值得背下来。权限类的工单八成以上是死在路径不一致或者漏绑定上剩下的才是真正复杂的模型问题。先把简单问题排除干净再去碰模型层面的事。4.3 前端路由守卫菜单渲染与页面访问的双重控制前端层面不是把所有路由都注册好再按权限隐藏而是只把当前用户菜单树里有的页面注册成路由。这意味着用户直接输URL访问一个没分配给他的页面前端会因为没有这个路由而跳转404或空白。这个行为容易给人造成一种错觉前端已经是安全的了。但实际上前端路由守卫只是一种体验层面的控制它保护的是看不见不是进不去。即使有人绕过前端直接向后端发HTTP请求只要Casbin规则不对后端照样返回403。做安全校验永远以服务端为准这一点做权限设计时心里要有底线。4.4 实际排查案例菜单可见但接口403、角色分配了还是404这次实际遇到的两个案例几乎能覆盖最常见的权限排查场景。案例A运营专员能看到内容管理菜单但进入页面后列表接口全部403。打开casbin_rule表一查这个角色只有菜单分配记录API规则一条都没有。因为运营专员是新建的角色初始化时不会自动绑定任何API必须手工在API管理里把内容管理的列表、详情、新建、编辑这些接口都绑定到角色上。这个案例提醒我菜单配置和API配置是两件事缺一不可。案例B管理员给某用户新增了一个角色用户反馈刷新了好几次还是看不到新菜单。排查到最后原因是用户登录时的token里存的还是旧角色集合旧角色里没有新增的菜单数据。让他退出重新登录菜单立刻出现了。这个案例的价值在于权限配置完不等于用户侧立即生效JWT会话机制决定了多数权限变更需要重新登录这不是bug是设计上线前要跟业务方说清楚。5. 线上部署绕不开的权限细节文件系统特殊权限与属性管理5.1 上传目录与静态资源的属主和权限权限管理做到后面真正在线上翻车的往往不是应用层RBAC而是文件系统那一层。GVA项目里最常见的两个问题上传文件失败、静态资源访问403十次有九次都出在目录权限上。GVA的上传功能一般会把文件写到服务器上的某个目录比如 uploads/ 或者 public/upload。如果这个目录的属主不是Web服务的运行用户或者目录没有写权限后端保存文件时就会报错。反过来如果nginx进程读取静态文件时没有对应目录的可执行权限前端访问就会403。这里说的可执行权限在目录上指的是能否进入该目录很容易被忽视。我建议在部署脚本里显式做这几件事mkdir -p /data/gva/uploads chown -R www:www /data/gva/uploads chmod 755 /data/gva/uploads chmod 644 /data/gva/uploads/*.png目录需要755是为了保证能进入文件644保证可读。如果上传目录里还有用户之间共享的私有文件不想让其他系统用户读取可以收紧到750或700但前提是运行用户和nginx用户都有对应权限。权限不是越严越好而是刚好够用。我还见过一个隐蔽问题上传成功但访问时404。这种情况通常是nginx的root或alias配置与后端返回的文件URL前缀对不上不是权限问题但很容易被误判成权限问题。排查时先分清是nginx找不到文件还是没有权限读文件别上来就改权限一通乱试。5.2 用chattr保护配置和日志特殊文件属性的实际价值常规的rwx权限在root面前基本是透明的root可以无视这些权限读改任何文件。但Linux文件系统还有一层特殊属性root也没办法轻易绕过这就是chattr管理的属性位。对GVA线上部署来说最有用的两个属性是i和a。config.yaml里包含了数据库密码和JWT签名密钥属于最高敏感度的配置文件。把它设为不可修改属性可以有效防止各种误操作chattr i /data/gva/config.yaml加了i属性后即使root想修改这个文件也会被拒绝。日常更新配置时先执行 chattr -i config.yaml改完再 chattr i config.yaml。这个习惯能帮你规避掉一大批手滑事故。我记得第一次给客户线上环境加这个属性时他们运维还跑来问是不是文件被锁死了解释清楚之后反而成了他们团队的一个标准操作。日志文件则更适合用追加属性a。日志本身应该只追加、不允许篡改和回写chattr a 可以保证即使进程出了bug或者有人故意搞破坏也没办法覆盖历史日志内容。不过要注意如果你用的日志切割方案是按大小rename文件比如logrotate的默认策略a属性会因为不允许重命名而报错。所以我建议日志目录别整体加a只对确定要长期保留的归档日志做保护。5.3 特殊权限位setuid、setgid、sticky bit与GVA部署场景文件系统还有一组特殊权限位经常在运维面试题里出现但在GVA部署中也有实际用途值得提一下setgid位符号s出现在属组执行位上数字2如果一个目录设置了setgid那在该目录下新建的文件会自动继承目录的属组而不是创建者自己的属组。GVA如果部署了多个服务实例都往同一个上传目录写文件设置setgid可以保证所有新文件都是同一个组后续权限管理更可控。命令是 chmod gs /data/gva/uploads。sticky bit符号t出现在其他用户执行位上数字1典型代表就是/tmp。设置后目录里每个用户只能删除和重命名自己的文件。如果你在服务器上给外部合作方开了一个公共交换目录又不希望别人互相删文件就加上 chmod t /data/gva/exchange。setuid位符号s出现在属主执行位上数字4这个在GVA部署中强烈不建议碰。它的含义是让普通用户以文件属主身份运行程序一旦被滥用就是提权漏洞。Web服务进程不需要setuid默认关闭就好。如果有安全扫描报告说某个文件有不正常的s位正确做法是把它去掉。查看特殊权限位的命令是statstat -c %a %A %U:%G %n /data/gva/config.yaml%a输出数字权限%A会显示类似 rw-r--r-- 的符号形式其中s和t就是特殊权限位。部署完成后用这条命令把关键路径扫一遍可以在几分钟内发现权限配置的明显问题。GVA本身是应用层框架不会直接管理这些文件权限但你的项目跑在Linux上文件系统这一层权限管不好前面做的所有应用权限都是白搭。我在GVA上把权限链路完整走通之后最大的感受是这个框架的权限设计其实不复杂但它把很多隐性依赖埋在了默认数据、配置文件、token有效期、服务器文件权限这些细节里。你如果只是照着手册点一遍很容易掉进配置好像完成了实际完全不可用的陷阱。我自己现在做GVA项目都会在第一天先建一个测试角色、配一个最小菜单和最小API、建一个测试用户把整条链路跑通再开始写业务。这样做能在项目早期就把权限模型里的坑排掉而不是等到上线前才手忙脚乱地全家桶排查。希望这篇流程记录能帮你少走这些弯路。
返回列表