编程 权限判断从几个 if 变成散落的判断之后:Casbin 与自研 RBAC 的取舍

2026-08-27 21:08:03 views 7

权限判断从几个 if 变成散落的判断之后:Casbin 与自研 RBAC 的取舍

几乎所有后台系统都会走到这一步:一开始只有管理员和普通用户,代码里一个 if user.Role == "admin" 就完了。后来产品开始提更细的需求,运营能改商品、财务只能看订单不能删、客服只能查单、区域管理员只管自己的区域,多租户场景下还要按租户隔离数据。权限判断从一个 if 扩散成 Handler、Service、Middleware 里散落的判断。

这时候真正的问题已经不是"怎么判断管理员",而是:谁能对什么做什么,如何长期、统一地维护下去。

认证与授权是两件事

认证(Authentication)解决"你是谁",常见手段是 JWT、Session、OAuth。

授权(Authorization)解决"你能做什么",比如:

userId = 10001
GET /orders     -> Allow
DELETE /orders  -> Deny

Casbin 管的是 Authorization,不负责登录、不管用户体系、不是 OAuth Server。典型链路是:

Request -> JWT Auth -> 获取 userId -> Casbin 判断 -> Allow/Deny

ACL 是最基础的抽象

ACL(Access Control List)把权限抽象成三元组:

sub -> 谁
obj -> 对什么
act -> 做什么

对应到请求就是 alice, /orders, GET,表示 Alice 可以对订单执行读操作。

但 ACL 在真实项目里很难直接用。用户一多,逐条分配权限的维护成本就上去了。于是出现了 RBAC。

RBAC:用户 -> 角色 -> 权限

RBAC 的核心变化是中间加了角色层:

User -> Role -> Permission

admin    -> order:read, order:create, order:update, order:delete
operator -> order:read, order:update
viewer   -> order:read

用户只绑定角色:Alice -> adminBob -> operator。权限管理从"用户到权限"变成了"用户到角色再到权限"。

自己写 RBAC 会遇到什么

第一版很简单,三张表的事:userrolepermission,加中间表 user_rolerole_permission,然后查用户有没有某个权限。

但需求不会停在第一版,很快会遇到这些:

角色继承。产品说 super_admin 自动拥有 admin 的权限,admin 又继承 operator。自己实现就要处理递归查询、循环继承、权限合并。Casbin 里直接声明角色关系即可,继承逻辑交给模型处理:

g, alice, admin
g, admin, operator

资源开始复杂。权限对象从 order:read 变成 /orders/:id 这种路径,还要区分 GET/POST/PUT/DELETE,甚至需要通配符和正则匹配。这些逻辑全部自己写,权限模块会逐渐变成一个规则引擎。

多租户。同一个用户在不同租户下角色不同:

Alice -> Tenant A -> admin
Alice -> Tenant B -> viewer

原来的 User -> Role 不够了,需要 User -> Tenant -> Role。Casbin 支持 domain RBAC,请求定义变成 r = sub, dom, obj, act,判断逻辑变成:

Alice, tenant-a, /orders, DELETE

除了上面这些,还有资源归属判断、ABAC 属性规则、多实例状态同步。到这一步,你其实已经在重新实现一个访问控制引擎,而不是在写权限表。

Casbin 的核心思想:Model 配置化

Casbin 做了一件很关键的事:把访问控制模型本身配置化。一个基础 RBAC 模型长这样:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
  • [request_definition] 定义一次权限判断包含什么
  • [policy_definition] 定义策略的结构
  • [role_definition] 定义角色关系
  • [policy_effect] 定义多个 policy 命中时的最终结果
  • [matchers] 定义匹配规则

一旦模型定了,业务代码只调 Enforce

ok, err := enforcer.Enforce("alice", "/orders", "GET")

策略数据长这样:

p, admin, /orders, GET
p, admin, /orders, POST
g, alice, admin

Alice 的每次请求,Casbin 根据模型和策略算出 Allow 或 Deny。

把模型配置化的好处是,权限变化时不用改业务代码。今天只是 RBAC,明天要加 Tenant,后天要根据资源 Owner 做 ABAC,改的是模型和策略,不是藏在 Handler 里的 if。

Casbin 不能替代权限表

管理后台仍然需要用户、角色、菜单、权限这些表。角色列表、菜单树、用户角色分配这些功能都得有数据支撑。Casbin 替代的是运行时"是否允许"这个判断逻辑,不是权限数据的管理和展示。

什么时候不需要 Casbin

如果系统永远只有 admin 和 user 两种角色,权限判断就是:

if user.IsAdmin {
    // allow
}

直接写更简单,引入 Casbin 是过度设计。

更精确的边界是:

  • 权限模型固定不变、角色极少 -> 自己写
  • 出现 Role + Resource + Action 的组合,或者 Tenant + Role + Resource + Action -> 考虑 Casbin

取舍

认证(Authentication)和业务规则适合自己掌控,因为它们和业务耦合度高。但通用 RBAC 判断涉及角色继承、路径匹配、多租户域隔离、策略演进,是一个成熟问题域,自己从头实现一个权限引擎大概率会踩坑。

对于准备长期维护的 Go 后台、SaaS 或微服务项目,RBAC 这类通用权限判断,没有必要再造轮子。

复制全文 生成海报 Casbin RBAC Go 权限 多租户

推荐文章

程序员茄子在线接单