Go Casbin 学习
Casbin 基础概念
什么是 Casbin
Casbin 是一个授权库,用于处理各种复杂的访问控制场景。
Casbin - 中文文档它支持:
- ACL (访问控制列表)
- RBAC (基于角色的访问控制)
- ABAC (基于属性的访问控制)
核心概念
- 模型(Model) : 定义访问控制策略的抽象表示
- 策略(Policy) : 具体的访问规则
- Enforcer: 执行器,负责加载模型和策略,执行访问控制决策
在 Casbin 的授权流程中:
- [matchers]先执行:负责筛选出所有相关的策略规则
- [policy_effect]后执行:负责解释这些匹配到的规则
安装 Casbin
go get github.com/casbin/casbin/v2最简单的 ACL 示例
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
)
func main() {
// 创建enforcer,加载模型和策略文件
e, err := casbin.NewEnforcer("model.conf", "policy.csv")
if err != nil {
panic(err)
}
// 检查权限
sub := "alice" // 用户
obj := "data1" // 资源
act := "read" // 操作
ok, err := e.Enforce(sub, obj, act)
if err != nil {
panic(err)
}
if ok {
fmt.Println("允许访问")
} else {
fmt.Println("拒绝访问")
}
}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.actACL 策略文件
p, alice, data1, read
p, bob, data2, write模型、策略文件配置理解
model.conf 配置
model.conf - 模型定义文件
模型文件定义了访问控制的基本结构和逻辑,它由多个部分组成。
[request_definition] - 请求定义
[request_definition]
r = sub, obj, act定义了访问请求的格式,通常包含三个元素:
- sub(subject): 访问主体,通常是用户
- obj(object): 访问资源/对象
- act(action): 操作/权限
可以扩展更多字段,如:r = sub, obj, act, time
[policy_definition] - 策略定义
[policy_definition]
p = sub, obj, act定义了策略的存储格式,与请求定义对应
在 RBAC 中可能还包含角色:p = sub, obj, act 或 p = role, obj, act
[role_definition] - 角色定义
RBAC 专用
[role_definition]
g = _, _这行配置定义了:
g 是角色关系的名称(可以自定义,如 group)
_,_表示关系的两端:用户, 角色
实际意义
这表示:
- 第一个 _代表用户(或子角色)
- 第二个 _代表角色(或父角色)
例如在策略中:
g, alice, admin表示:用户 alice 拥有 admin 角色。
[policy_effect] - 策略效果
定义了多个策略规则如何组合生效。
eft 是"effect"的缩写,表示这条策略规则的效果。
不看用户名:策略效果评估时,不区分是哪个用户的规则。
只看规则存在:只要存在任何 deny 规则,!some(deny)就会为 false。
常见选项:
- some(where (p.eft == allow)):有一条允许则允许(类似 OR 逻辑)
- !some(where (p.eft == deny)):没有拒绝则允许
- some(where (p.eft == allow)) && !some(where (p.eft == deny))
[policy_effect]
e = some(where (p.eft == allow))策略效果详解
some(where (p.eft == allow))
含义:
- 检查所有匹配的策略规则
- 只要有一条规则的效果是 allow,就允许访问
- 类似 OR 逻辑(多条允许规则中有一条成立即可)
示例:
p, alice, data1, read, allow
p, alice, data1, read, deny结果:允许访问(因为有一条 allow 规则)
!some(where (p.eft == deny))
含义:
- 检查所有匹配的策略规则
- 如果没有任何规则的效果是 deny,就允许访问
- 类似"没有反对就通过"的逻辑
示例 1:
p, alice, data1, read, allow
p, bob, data1, read, allow结果:允许访问(因为没有 deny 规则)
示例 2:
p, alice, data1, read, allow
p, bob, data1, read, allow
p, alice, data1, read, deny结果:拒绝访问(因为有 deny 规则)
some(where (p.eft == allow)) && !some(where (p.eft == deny))
含义:
- 必须同时满足两个条件:
- 至少有一条规则是 allow
- 没有任何规则是 deny
- 这是最严格的策略效果,常用于需要明确允许且没有拒绝的场景
示例 1:
p, alice, data1, read, allow
p, bob, data1, read, allow结果:允许访问(有 allow 且没有 deny)
示例 2:
p, alice, data1, read, deny
p, bob, data1, read, deny结果:拒绝访问(没有 allow)
[matchers]-匹配器
定义了请求如何匹配策略的核心逻辑。
- 可以使用逻辑运算符:&&, ||, !
- 可以使用内置函数或自定义函数
- 在 RBAC 中通常包含角色检查:
- m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act- r.sub == p.sub:请求的主体(subject)必须匹配策略的主体
- r.obj == p.obj:请求的资源(object)必须匹配策略的资源
- r.act == p.act:请求的操作(action)必须匹配策略的操作
policy.csv 配置
策略文件存储了具体的访问控制规则,其格式由模型文件中的[policy_definition]决定。
基本 ACL 策略
对于基本 ACL 模型:
p, alice, data1, read
p, bob, data2, write每行代表一条策略规则
- 格式:p, sub, obj, act
- p:表示这是策略规则(由[policy_definition]定义)
- 后续字段对应模型定义
RBAC 策略
对于 RBAC 模型,通常有两类策略:
- 权限分配策略:
p, admin, data1, read
p, admin, data1, write
p, user, data1, read表示角色对资源的权限
- 用户角色分配策略:
g, alice, admin
g, bob, user- g 表示角色分配(由[role_definition]定义)
- 表示用户拥有的角色
策略文件中的特殊语法
通配符 :可以使用*匹配任意值
p, admin, *, *否定:可以在 eft 字段指定 allow/deny
p, alice, data1, read, deny优先级:可以使用优先级数字
p, admin, data1, read, allow, 1配置示例解析
ACL 示例
model.conf
[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.actpolicy.csv
p, alice, data1, read
p, bob, data2, write逻辑解释 :
- 当请求(alice, data1, read)时:
- 匹配第一条策略 p, alice, data1, read
- r.sub == p.sub(alice == alice)
- r.obj == p.obj(data1 == data1)
- r.act == p.act(read == read)
- 匹配成功,允许访问
- 当请求(alice, data2, read)时:
- 没有匹配的策略
- 拒绝访问
RBAC 示例
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.actpolicy.csv
p, admin, data1, read
p, admin, data1, write
p, user, data1, read
g, alice, admin
g, bob, user逻辑解释 :
- 当请求(alice, data1, read)时:
- g(r.sub, p.sub)检查 alice 的角色
- alice 是 admin(通过 g, alice, admin)
- 查找 admin 的权限:
- p, admin, data1, read 匹配
- 允许访问
- 当请求(bob, data1, write)时:
- bob 是 user 角色
- user 角色只有 data1 的 read 权限
- 没有匹配的策略
- 拒绝访问
理解模型和策略的关系
- 模型(model.conf) :相当于"宪法",定义了整个访问控制系统的规则框架
- 定义了"如何判断"权限
- 定义了策略的存储格式
- 定义了角色系统的工作方式
- 策略(policy.csv) :相当于"法律条文",是具体的规则实例
- 存储了谁(用户/角色)对什么资源有什么权限
- 存储了用户-角色映射关系
- 可以根据模型定义的格式灵活扩展
RBAC 示例
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
)
func main() {
e, err := casbin.NewEnforcer("rbac_model.conf", "rbac_policy.csv")
if err != nil {
panic(err)
}
// 检查用户角色
roles, err := e.GetRolesForUser("alice")
if err != nil {
panic(err)
}
fmt.Println("Alice的角色:", roles)
// 检查权限
sub := "alice"
obj := "data2"
act := "read"
ok, err := e.Enforce(sub, obj, act)
if err != nil {
panic(err)
}
if ok {
fmt.Println("允许访问")
} else {
fmt.Println("拒绝访问")
}
}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.actRBAC 策略文件
p, admin, data1, read
p, admin, data1, write
p, admin, data2, read
p, admin, data2, write
p, user, data1, read
g, alice, admin
g, bob, user使用数据库存储策略
实际项目中,我们通常将策略存储在数据库中:
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
"github.com/casbin/gorm-adapter/v3"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 初始化数据库连接
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
// 创建适配器
adapter, err := gormadapter.NewAdapterByDB(db)
if err != nil {
panic(err)
}
// 创建enforcer
e, err := casbin.NewEnforcer("rbac_model.conf", adapter)
if err != nil {
panic(err)
}
// 添加策略
e.AddPolicy("admin", "data1", "read")
e.AddPolicy("admin", "data1", "write")
e.AddRoleForUser("alice", "admin")
// 保存策略
err = e.SavePolicy()
if err != nil {
panic(err)
}
// 检查权限
ok, err := e.Enforce("alice", "data1", "read")
if err != nil {
panic(err)
}
fmt.Println("允许访问:", ok)
}高级功能
ABAC (基于属性的访问控制)
Casbin 支持 ABAC,可以根据对象的属性进行访问控制:
type Book struct {
Name string
Author string
}
func main() {
e, err := casbin.NewEnforcer("abac_model.conf")
if err != nil {
panic(err)
}
book := Book{Name: "Casbin指南", Author: "alice"}
// 检查权限
ok, err := e.Enforce("alice", book, "read")
if err != nil {
panic(err)
}
fmt.Println("允许访问:", ok)
}ABAC 模型文件 (abac_model.conf):
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == r.obj.Author && r.act == p.act使用函数进行复杂匹配
可以在模型中使用自定义函数:
e, err := casbin.NewEnforcer("model_with_func.conf")
if err != nil {
panic(err)
}
// 添加自定义函数
e.AddFunction("timeMatch", func(args ...interface{}) (interface{}, error) {
// 实现时间匹配逻辑
return true, nil
})然后在模型中使用:
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act && timeMatch(r.time)调试技巧
使用 Enforcer 的打印方法查看加载的模型和策略 :
fmt.Println(e.GetModel())
fmt.Println(e.GetPolicy())检查角色映射关系 :
roles, _ := e.GetRolesForUser("alice")
fmt.Println(roles)启用日志 :
e.EnableLog(true)