Go 与 Casbin 技术实践:从权限模型到代码落地

在后端系统开发中,权限控制几乎是绕不开的话题。一个稍微复杂一点的系统,往往都会遇到这样几个问题:谁可以访问某个接口?谁可以操作某类资源?管理员和普通用户的权限边界如何划分?如果继续通过大量 if-else 或硬编码的方式维护权限逻辑,代码会很快变得混乱且难以扩展。

这时,Casbin 就是一个非常值得引入的权限控制框架。

Casbin 是一个强大的、支持多种访问控制模型的开源权限管理库,支持 ACL、RBAC、ABAC 等主流模型,并且可以很好地与 Go 项目集成。本文会先介绍 Casbin 的核心模型,再通过一个 Go 示例展示如何与 Casbin 进行交互。


一、Casbin 是什么

Casbin 是一个统一的访问控制框架,它将“权限判断逻辑”从业务代码中抽离出来,让开发者通过“模型 + 策略”的方式来定义访问控制规则。

简单来说,Casbin 主要解决的是下面这个问题:

某个用户 sub,是否可以对某个资源 obj 执行某个操作 act

例如:

  • alice 是否可以读取 data1
  • bob 是否可以写入 data2
  • admin 是否可以删除某个订单

Casbin 并不要求你把权限判断逻辑写死在代码里,而是通过配置文件来表达规则,这样系统的扩展性和维护性都会更高。


二、Casbin 的核心组成

Casbin 的权限控制通常由两部分组成:

  1. 模型(Model):定义权限判断的结构和逻辑
  2. 策略(Policy):定义具体的权限数据

可以把它理解为:

  • 模型 决定“权限判断规则长什么样”
  • 策略 决定“谁拥有什么权限”

例如,一个典型的权限判断语句如下:

alice, data1, read

它可以表示:用户 alice 对资源 data1 拥有 read 权限。

而 Casbin 会根据模型中的匹配逻辑,判断这条策略是否满足访问请求。


三、Casbin 模型详解

Casbin 的模型通常定义在一个 .conf 文件中。对于初学者来说,最常见的是 RBAC 或 ACL 风格模型。

下面是一个比较经典的模型示例:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

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

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

下面分别解释这四个部分。

1. request_definition

[request_definition]
r = sub, obj, act

这部分定义了“请求”的结构。

也就是说,每次做权限校验时,Casbin 都会接收一个请求,这个请求由三个字段组成:

  • sub:主体(subject),通常表示用户、角色或系统主体
  • obj:资源对象(object),通常表示接口、文件、订单、菜单等资源
  • act:操作(action),通常表示 read、write、delete 等动作

例如:

ok, err := e.Enforce("alice", "data1", "read")

这里的:

  • alice 对应 sub
  • data1 对应 obj
  • read 对应 act

2. policy_definition

[policy_definition]
p = sub, obj, act

这部分定义了“策略”的结构。

策略可以理解为一条条权限记录。例如:

p, alice, data1, read
p, bob, data2, write

表示:

  • alice 可以读取 data1
  • bob 可以写入 data2

p 就代表一条 policy 规则。它的字段结构需要和模型中的 p = sub, obj, act 对应起来。


3. policy_effect

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

这部分定义了策略生效方式,也就是“多条策略匹配时,最终如何决策”。

这句的含义是:

只要存在一条策略结果是 allow,就允许访问。

这是 Casbin 中最常见的一种策略效果。

在实际项目中,你还可能看到更复杂的效果表达式,例如同时处理 allowdeny,但对于入门来说,先理解“命中一条允许规则即可放行”就足够了。


4. matchers

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

这部分是 Casbin 模型里最核心的部分,它定义了“请求”和“策略”如何进行匹配。

这句的意思很直白:

  • 请求中的 sub 必须等于策略中的 sub
  • 请求中的 obj 必须等于策略中的 obj
  • 请求中的 act 必须等于策略中的 act

只有三者都匹配,Casbin 才会认为该请求被这条策略命中。

例如:

请求:

(alice, data1, read)

策略:

(alice, data1, read)

那么匹配成功,返回允许。

但如果请求变成:

(alice, data1, write)

由于 act 不匹配,因此不会通过。


四、Casbin 中的 RBAC 思想

上面的模型更接近 ACL。实际开发中,更常见的是 RBAC(基于角色的访问控制)

RBAC 的核心思想是:

不直接给用户分配权限,而是先给角色分配权限,再把用户绑定到角色上。

这样做的好处是权限管理更加灵活。

例如:

  • admin 角色拥有全部后台管理权限
  • editor 角色拥有文章编辑权限
  • guest 角色只有查看权限

用户只需要绑定角色即可。

一个简单的 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

这里的关键点在于:

g(r.sub, p.sub)

它表示:请求中的用户 r.sub 是否具有策略中 p.sub 这个角色。

例如:

p, admin, /user/list, read
p, admin, /user/create, write
g, alice, admin

表示:

  • admin 角色可以读取 /user/list
  • admin 角色可以写入 /user/create
  • alice 拥有 admin 角色

因此,alice 也就具备了对应权限。


五、Go 中如何集成 Casbin

下面通过一个简单示例,看看 Go 如何与 Casbin 交互。

1. 安装依赖

先在项目中安装 Casbin:

go get github.com/casbin/casbin/v2

2. 编写模型文件 model.conf

[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

这个模型使用的是 RBAC。


3. 编写策略文件 policy.csv

p, admin, /user/list, read
p, admin, /user/create, write
p, user, /profile, read
g, alice, admin
g, bob, user

这表示:

  • admin 角色可以读取用户列表、创建用户
  • user 角色可以读取个人信息
  • alice 属于 admin
  • bob 属于 user

4. Go 示例代码

下面是一段完整的 Go 示例代码:

package main

import (
	"fmt"
	"log"

	"github.com/casbin/casbin/v2"
)

func main() {
	e, err := casbin.NewEnforcer("model.conf", "policy.csv")
	if err != nil {
		log.Fatalf("failed to create enforcer: %v", err)
	}

	checkPermission(e, "alice", "/user/list", "read")
	checkPermission(e, "alice", "/user/create", "write")
	checkPermission(e, "bob", "/profile", "read")
	checkPermission(e, "bob", "/user/create", "write")
}

func checkPermission(e *casbin.Enforcer, sub, obj, act string) {
	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
		log.Printf("enforce error: sub=%s obj=%s act=%s err=%v", sub, obj, act, err)
		return
	}

	fmt.Printf("user=%s, obj=%s, act=%s, allowed=%v\n", sub, obj, act, ok)
}

六、代码解析

这段代码的核心流程并不复杂。

1. 创建 Enforcer

e, err := casbin.NewEnforcer("model.conf", "policy.csv")

Enforcer 可以理解为 Casbin 的执行器。它负责:

  • 加载模型配置
  • 加载策略数据
  • 根据模型匹配规则执行权限判断

在项目运行过程中,几乎所有权限判断都要通过 Enforcer 来完成。


2. 执行权限校验

ok, err := e.Enforce(sub, obj, act)

这是 Casbin 最核心的调用方式。

Enforce 会根据你传入的请求参数:

  • 当前访问者是谁
  • 正在访问什么资源
  • 想执行什么操作

然后去匹配模型和策略,最终返回一个布尔值:

  • true:允许访问
  • false:拒绝访问

例如:

ok, _ := e.Enforce("alice", "/user/list", "read")

由于 alice 绑定了 admin 角色,而 admin 拥有 /user/listread 权限,因此返回 true

而:

ok, _ := e.Enforce("bob", "/user/create", "write")

由于 bob 只绑定了 user 角色,不具备该权限,因此返回 false


七、运行结果示例

执行程序后,你可能看到类似输出:

user=alice, obj=/user/list, act=read, allowed=true
user=alice, obj=/user/create, act=write, allowed=true
user=bob, obj=/profile, act=read, allowed=true
user=bob, obj=/user/create, act=write, allowed=false

这正符合我们在策略中定义的权限规则。


八、Casbin 在实际项目中的常见用法

在真实项目里,Casbin 通常不会只停留在这个简单示例层面,而是会进一步结合以下场景:

1. 与 Web 框架中间件结合

在 Gin、Echo、Fiber 等 Go Web 框架中,Casbin 常常被封装为中间件:

  • 请求进入时先解析当前用户身份
  • 再提取请求路径和请求方法
  • 最后调用 Enforce 判断是否允许访问

例如,可以把:

  • 用户 ID / 角色 作为 sub
  • URL 路径作为 obj
  • HTTP 方法(GET、POST、PUT、DELETE)作为 act

这样就能对接口做统一权限控制。

2. 使用数据库存储策略

入门示例常用 policy.csv 保存策略,但在生产环境中,权限规则通常存放在数据库中,以便动态维护。

例如:

  • 后台管理系统修改角色权限
  • 实时增删策略
  • 多实例服务共享统一权限数据

Casbin 提供了 Adapter 机制,可以把策略存储到 MySQL、PostgreSQL、SQLite 等数据库中。

3. 支持更复杂的权限模型

除了 RBAC,Casbin 还支持:

  • ACL:面向具体用户授权
  • ABAC:基于属性进行权限控制
  • RBAC with Domains:支持租户、多组织、多空间场景

因此它不仅适合简单后台系统,也适合中大型 SaaS 系统。


九、为什么在 Go 项目中推荐 Casbin

对于 Go 项目而言,Casbin 有几个非常明显的优势:

1. 解耦权限逻辑与业务逻辑

如果把权限判断写在业务代码中,随着接口增多,代码会越来越难维护。而 Casbin 把权限规则抽离出来,让业务代码只关注业务本身。

2. 模型灵活

Casbin 并不是只支持一种固定的权限模式,而是允许开发者通过模型文件定义自己的规则。

3. 易于扩展

无论是从文件策略升级到数据库策略,还是从简单 ACL 升级到 RBAC、多租户模型,Casbin 都能平滑演进。

4. 与 Go 生态结合自然

Casbin 的 Go 版本使用非常直接,API 清晰,适合快速接入现有服务。


十、总结

Casbin 的核心价值在于:它通过“模型 + 策略”的方式,把权限控制从业务代码中独立出来,让系统权限管理更加清晰、灵活和可维护。

对于初学者来说,理解 Casbin 最重要的是先掌握下面几点:

  • request_definition 定义请求结构
  • policy_definition 定义策略结构
  • policy_effect 定义策略生效方式
  • matchers 定义请求与策略的匹配逻辑

在 Go 中使用 Casbin 也非常简单,通常只需要:

  1. 定义模型文件
  2. 定义策略文件
  3. 创建 Enforcer
  4. 调用 Enforce 做权限判断

如果你的项目正面临权限逻辑越来越复杂、角色越来越多、接口访问控制越来越难维护的问题,那么 Casbin 会是一个非常值得引入的解决方案。


附:一个更贴近接口权限控制的思路

在实际 Web 项目中,你还可以把接口权限设计为:

  • sub:当前用户角色,如 admineditorguest
  • obj:接口路径,如 /api/orders
  • act:HTTP 方法,如 GETPOSTDELETE

例如策略可以写成:

p, admin, /api/orders, GET
p, admin, /api/orders, POST
p, editor, /api/orders, GET
p, guest, /api/orders, GET

这样 Casbin 就可以很好地服务于 RESTful API 权限控制场景。

如果后续继续深入,你还可以进一步研究:

  • Casbin 与 Gin 中间件整合
  • Casbin 的数据库 Adapter
  • Casbin 的 RBAC 多租户模型
  • 基于路径匹配的权限控制(如 keyMatch

这些能力可以让 Casbin 在企业级 Go 项目中发挥更大的价值。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐