优先选RBAC:角色、资源、操作关系清晰,建模简单,中间件易集成;ABAC在Go中实现成本高且多数业务无需其灵活性;避免过早引入Casbin或复杂策略表。

权限模型选 RBAC 还是 ABAC?Go 里别硬套概念
Go 没有内置权限框架,直接套用 RBAC(基于角色)或 ABAC(基于属性)术语容易写偏。实际落地时,RBAC 更适合多数后台系统——角色、资源、操作三者关系清晰,数据库建模简单,gorilla/mux 或 gin 中间件也容易注入校验逻辑。ABAC 虽灵活,但在 Go 里意味着你要自己解析策略表达式、做运行时属性求值,rego 集成成本高,且多数业务根本用不到动态属性决策。
常见错误:一上来就设计 Policy 表存 JSON 策略,结果连「用户能否编辑自己创建的文章」这种基础判断都要查库+反序列化+执行,性能差还难 debug。
- 起步用 RBAC:定义
Role、Permission、role_permission关联表即可 - 把「资源 ID + 用户 ID + 操作类型」作为校验入口,比如
CanUserDo(ctx, userID, "update", "post:123") - 避免在中间件里做复杂策略计算;把权限判定下沉到 service 层,方便单元测试
用 go-chi 或 gin 写权限中间件,别碰 http.Handler 原生写法
手写 http.Handler 包装器容易漏掉 panic 恢复、上下文传递、错误响应格式统一等问题。直接用 chi.MiddlewareFunc 或 gin.HandlerFunc 更稳。
典型坑:中间件里调用 r.Context().Value() 取用户信息,但没在认证中间件里塞进去,导致后续全链路 panic;或者用 ctx.WithValue 塞了,却忘了用自定义 key 类型(用了 string),造成 key 冲突。
立即学习“go语言免费学习笔记(深入)”;
- 认证中间件必须先于权限中间件注册,顺序错就等于裸奔
- 权限中间件里别直接 return,用
c.AbortWithStatusJSON(403, ...)(gin)或http.Error(w, ..., 403)(chi)并立即中断 - 资源路径匹配别依赖正则硬编码,比如
/api/v1/posts/{id},应从路由参数取id,而不是从 URL 字符串里 parse
casbin 是不是银弹?它在 Go 项目里的真实代价
casbin 能省掉权限规则建模的重复劳动,但它不是零成本。默认用 file adapter 时,每次策略变更要 reload 文件;用 gorm-adapter 则每查一次权限都可能触发 3–5 次 SQL 查询(role → role-permission → permission → resource),QPS 上千就吃紧。
更隐蔽的问题:casbin.Enforcer 实例不是线程安全的,多 goroutine 并发调用 Enforce 时,若没做初始化保护或复用不当,会 panic 报 "invalid memory address"。
- 上线前压测真实权限路径,比如
e.Enforce("alice", "post:1001", "delete"),看 P95 延迟是否超 5ms - 避免在 HTTP handler 里每次都
newEnforcer,全局单例 +LoadPolicy一次就够了 - 如果策略不常变,用
cached-enforcer或自己加一层 map[string]bool 缓存热路径
数据库权限字段怎么设计?别让 is_admin 毁掉扩展性
加个 is_admin bool 字段最省事,但只要产品提一句「运营人员只能删评论,不能删用户」,你就得改表、改逻辑、改所有 if 判断。真正的扩展性来自解耦:用户 ↔ 角色 ↔ 权限 ↔ 资源操作。
常见翻车点:把权限字符串存成逗号分隔的 "read,write,delete" 在 user 表里,结果想查「哪些人能删文章」就得全表扫描 + 字符串匹配,索引完全失效。
- 权限粒度按 REST 动作 + 资源类型切分,如
post:read、user:ban、comment:delete - 用户表只存
role_id,权限查 Role 表关联 Permission 表,别冗余到用户行 - 需要特殊豁免(比如某管理员临时禁言某用户),走独立的
override_policy表,不污染主权限流
权限系统最难的从来不是写代码,而是每次产品说“这个按钮,只有张三能点”时,你得快速判断——这是该加个新权限项,还是该建个临时 override,还是其实暴露了模型缺陷。留好策略变更的观测点,比堆功能重要得多。


















