1. 认证和授权是两件事
第 6 篇的 JWT 解决「你是谁」(认证)。这一篇解决「你能干啥」(授权)。登录用户不一定能删文章,要按角色判断。
2. casbin 模型
model.conf 定义 RBAC 规则:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
sub 是角色/用户,obj 是资源(如 /articles),act 是操作(get/delete)。
3. policy 是具体授权
p, admin, /articles, delete
p, admin, /users, *
p, editor, /articles, post
admin 能删文章、管用户;editor 只能发文章。改权限只改 policy,不动代码。
4. 中间件鉴权
func Casbin(e *casbin.Enforcer) gin.HandlerFunc { return func(c *gin.Context) { role := c.GetString("role") // 来自第 6 篇 JWT 解析 ok, _ := e.Enforce(role, c.FullPath(), strings.ToLower(c.Request.Method)) if !ok { c.JSON(403, gin.H{"error": "无权限"}) c.Abort() return } c.Next() } }
JWT 中间件先跑填 role,casbin 再判断,两层串起来。
5. 和 JWT 衔接
登录时把角色写进 JWT claims(第 6 篇),鉴权中间件解出来当 sub:
claims := &Claims{} jwt.ParseWithClaims(token, claims, keyFunc) c.Set("role", claims.Role)
别在前端存角色当真理,每次请求服务端按 token 里的角色判,防伪造提权。
6. 动态权限
policy 存库,后台改了实时加载:
e.LoadPolicy() // 改完重读 e.AddPolicy("viewer", "/articles", "get") // 或直接加一条
小系统用文件 policy 够了,多租户/复杂权限存库 + 缓存。
7. 上线清单
认证(JWT)和授权(casbin)分开,别混。角色从 JWT claims 取,不自前端传。policy 改完重加载,动态权限走库。默认拒绝(没匹配到 policy 就 403),别默认放行。超级管理员和白名单接口单独处理,避免被 RBAC 误伤。
四篇走完,第四季把文档、性能、数据库、权限这四个生产必补的坑填上了。三季加这一季,Gin 后端从能写到能上线到能扛量到能管权限,齐了。