Go 中最强大的权限控制库(Casbin)
权限控制,它决定了 "谁能访问什么资源,能做什么操作"。
Casbin 是一个强大的、高效的开源访问控制框架,支持 ACL、RBAC、ABAC 等多种经典访问控制模型,通过配置文件即可灵活定义权限规则,同时支持策略的动态管理和持久化存储。
| 概念 | 作用 | 说明 |
|---|---|---|
| Model(模型) | 定义权限控制的逻辑 | 用 CONF 格式的文件或字符串编写,定义 "请求是什么、策略是什么、如何匹配、结果是什么" |
| Policy(策略) | 定义具体的权限规则 | 可以存储在文件、数据库中,定义 "谁(用户 / 角色)能对什么资源做什么操作" |
| Adapter(适配器) | 负责策略的存储和加载 | 支持文件、MySQL、PostgreSQL、Redis、MongoDB 等多种存储方式 |
| Enforcer(执行器) | Casbin 的核心组件 | 加载 Model 和 Policy,提供权限判断、策略管理等核心 API |
ACL(访问控制列表)
ACL 是最简单的访问控制模型,直接定义 "用户 → 资源 → 操作" 的权限关系,适合小型项目或简单的权限场景。
编写 Model 文件(model.conf)
Model 文件定义了权限控制的逻辑,Casbin 通过它来理解如何判断权限。
ini
[request_definition]
# 定义请求的格式:r = [主体(sub), 客体(obj), 动作(act)]
# sub:用户/角色;obj:资源;act:操作(如 read、write)
r = sub, obj, act
[policy_definition]
# 定义策略的格式:p = [主体(sub), 客体(obj), 动作(act)]
p = sub, obj, act
[policy_effect]
# 定义策略效果:e = some(where (p.eft == allow))
# 意思是:只要有一条策略匹配且效果为 allow,就允许访问
e = some(where (p.eft == allow))
[matchers]
# 定义匹配规则:m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
# 意思是:请求的 sub、obj、act 必须和策略完全一致才匹配
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
编写 Policy 文件(policy.csv)
Policy 文件定义了具体的权限规则,用 CSV 格式存储。
csv
# p, 主体, 客体, 动作
p, zhangsan, /api/user, read
p, zhangsan, /api/user, write
p, lisi, /api/article, read
上面的策略表示:
zhangsan可以对/api/user资源进行read和write操作lisi可以对/api/article资源进行read操作
初始化 Enforcer:加载 Model 文件和 Policy 文件
go
// 参数 1:Model 文件路径
// 参数 2:Policy 文件路径
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
fmt.Printf("初始化 Enforcer 失败:%v\n", err)
return
}
casbin.NewEnforcer(modelPath, policyPath):初始化 Enforcer,加载 Model 和 Policy。
权限判断:使用 Enforce 方法
go
// 参数顺序:sub, obj, act(和 Model 中 r 的定义一致)
// 场景 1:zhangsan 读取 /api/user
ok, err := e.Enforce("zhangsan", "/api/user", "read")
if err != nil {
fmt.Printf("权限判断失败:%v\n", err)
return
}
if ok {
fmt.Println("zhangsan 允许读取 /api/user")
} else {
fmt.Println("zhangsan 不允许读取 /api/user")
}
// 场景 2:lisi 写入 /api/article(策略中没有这条,应该拒绝)
ok, _ = e.Enforce("lisi", "/api/article", "write")
if ok {
fmt.Println("lisi 允许写入 /api/article")
} else {
fmt.Println("lisi 不允许写入 /api/article")
}
e.Enforce(sub, obj, act):核心权限判断方法,参数顺序必须和 Model 中r的定义一致,返回bool表示是否允许访问。
RBAC(基于角色的访问控制)
RBAC 是企业开发中最常用的权限模型,它通过 "角色" 作为中间层,将用户与权限解耦(用户 → 角色 → 权限),大大简化了权限管理(比如给用户分配角色即可获得该角色的所有权限,无需逐个分配)。
编写 RBAC Model 文件(rbac_model.conf)
RBAC 模型在 ACL 的基础上增加了角色定义(g) 和 角色继承。
ini
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
# 定义角色关系:g = [用户, 角色]
# 表示"用户属于某个角色"
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
# 匹配规则:g(r.sub, p.sub) 表示"请求的用户属于策略中的角色"
# 或者请求的用户直接等于策略中的主体(支持用户直接分配权限)
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
编写 RBAC Policy 文件(rbac_policy.csv)
-
角色权限:
admin可以对/api/user和/api/article进行read和writeeditor可以对/api/article进行read和writeviewer只能对/api/article进行read
-
用户角色:
zhangsan是adminlisi是editorwangwu是viewer
csv
# 1. 角色权限策略:p, 角色, 资源, 动作
p, admin, /api/user, read
p, admin, /api/user, write
p, admin, /api/article, read
p, admin, /api/article, write
p, editor, /api/article, read
p, editor, /api/article, write
p, viewer, /api/article, read
# 2. 用户角色策略:g, 用户, 角色
g, zhangsan, admin
g, lisi, editor
g, wangwu, viewer
动态管理用户角色
go
// 给 wangwu 添加 editor 角色(现在 wangwu 是 viewer + editor)
added, err := e.AddRoleForUser("wangwu", "editor")
if added {
// 保存策略到文件(生产环境用数据库适配器自动保存)
e.SavePolicy()
}
// 现在 wangwu 应该允许写入 /api/article 了
ok, _ = e.Enforce("wangwu", "/api/article", "write")
// 删除 wangwu 的 viewer 角色
removed, err := e.DeleteRoleForUser("wangwu", "viewer")
if removed {
e.SavePolicy()
}
动态管理权限
go
// 给 editor 角色添加 /api/user 的 read 权限
added, err = e.AddPolicy("editor", "/api/user", "read")
if added {
e.SavePolicy()
}
// 现在 lisi(editor)应该允许读取 /api/user 了
ok, _ = e.Enforce("lisi", "/api/user", "read")
| API | 作用 |
|---|---|
e.AddRoleForUser(user, role) |
给用户添加角色 |
e.DeleteRoleForUser(user, role) |
删除用户的角色 |
e.GetRolesForUser(user) |
获取用户的所有角色 |
e.GetUsersForRole(role) |
获取拥有该角色的所有用户 |
e.AddPolicy(sub, obj, act) |
添加权限策略 |
e.DeletePolicy(sub, obj, act) |
删除权限策略 |
e.SavePolicy() |
保存策略到存储(文件 / 数据库) |
RBAC with Domains(多租户 / 多部门)
如果你的系统是多租户(如 SaaS 平台)或多部门的,需要隔离不同租户 / 部门的权限,Casbin 支持 RBAC with Domains 模型,轻松实现权限隔离。
在 request_definition 和 matchers 中增加 dom(域 / 租户)字段:
ini
[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, r.dom, p.sub, p.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
权限判断和角色管理时需要增加 dom 参数:
go
// 权限判断:sub, dom, obj, act
e.Enforce("zhangsan", "tenant1", "/api/user", "read")
// 给用户添加租户下的角色
e.AddRoleForUserInDomain("zhangsan", "admin", "tenant1")
数据库适配器
在开发环境中,我们可以用文件存储 Policy,但在生产环境中,通常需要用数据库存储 Policy,方便动态管理和持久化。Casbin 提供了丰富的数据库适配器,这里以最常用的 GORM Adapter(配合 MySQL)为例。
go
// 1. 连接 MySQL 数据库
dsn := "root:your_password@tcp(127.0.0.1:3306)/casbin_demo?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// 2. 初始化 GORM Adapter
// 参数 1:GORM DB 实例
// 参数 2:是否自动迁移(创建 casbin_rule 表)
a, err := gormadapter.NewAdapterByDB(db)
// 3. 初始化 Enforcer:加载 Model 文件和数据库 Adapter
e, err := casbin.NewEnforcer("./rbac_model.conf", a)
// 4. 初始化策略(仅第一次运行时执行,后续可注释)
// 添加角色权限
e.AddPolicy("admin", "/api/user", "read")
e.AddPolicy("admin", "/api/user", "write")
// 添加用户角色
e.AddRoleForUser("zhangsan", "admin")
// 保存策略到数据库
e.SavePolicy()
// 5. 权限判断
ok, _ := e.Enforce("zhangsan", "/api/user", "write")
// 6. 动态修改策略(自动同步到数据库)
e.AddRoleForUser("wangwu", "editor")
ok, _ = e.Enforce("wangwu", "/api/article", "write")
- 自动同步 :使用数据库 Adapter 后,调用
AddPolicy、AddRoleForUser等 API 时,策略会自动同步到数据库,无需手动调用SavePolicy()(部分 Adapter 需要,GORM Adapter 自动同步)。 - 持久化:策略存储在数据库中,服务重启后不会丢失。
- 动态管理:可以通过数据库直接管理策略,或通过后台管理界面调用 Casbin API 管理。
最佳实践
Model 设计
- Model 文件独立存储 :将 Model 文件放在项目的
config目录下,不要硬编码在代码里。 - 从简单到复杂:优先使用 RBAC,只有在需要属性级权限控制时才用 ABAC。
- 合理使用通配符 :Casbin 支持
*通配符(如p, admin, *, *表示 admin 可以访问所有资源的所有操作),但要谨慎使用,避免权限过大。
策略管理
- 生产环境用数据库 Adapter:不要用文件存储 Policy,推荐用 GORM Adapter 或 Redis Adapter。
- 策略缓存:Casbin 内置了策略缓存,高并发场景下可大幅提升性能,默认开启。
- 定期备份策略:定期备份数据库中的策略表,防止误操作导致策略丢失。
性能优化
- 使用 EnforceContext :高并发场景下使用
EnforceContext替代Enforce,支持上下文传递和超时控制。 - 批量权限判断 :使用
BatchEnforce进行批量权限判断,性能更高。 - 合理设计索引 :如果使用数据库 Adapter,确保
casbin_rule表有合理的索引(GORM Adapter 会自动创建)。
安全注意事项
- 不要在策略中存储敏感信息:策略仅存储权限规则,不要存储密码、密钥等敏感信息。
- 最小权限原则:给用户和角色分配最小必要的权限,避免权限过大。
- 权限验证前置:在 Gin/Echo 等框架的中间件中统一进行权限验证,避免每个接口都写权限判断代码。