权限框架casbin
1.概述
Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型。
Casbin支持以下编程语言:
- Casbin可以做到:
- 支持自定义请求的格式,默认的请求格式为
{subject, object, action}
。- 具有访问控制模型model和策略policy两个核心概念。
- 支持RBAC中的多层角色继承,不止主体可以有角色,资源也可以具有角色。
- 支持超级用户,如
root
或Administrator
,超级用户可以不受授权策略的约束访问任意资源。- 支持多种内置的操作符,如
keyMatch
,方便对路径式的资源进行管理,如/foo/bar
可以映射到/foo*
- Casbin不能做到:
- 身份认证 authentication(即验证用户的用户名、密码),casbin只负责访问控制。应该有其他专门的组件负责身份认证,然后由casbin进行访问控制,二者是相互配合的关系。
- 管理用户列表或角色列表。 Casbin 认为由项目自身来管理用户、角色列表更为合适, 用户通常有他们的密码,但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系
github:https://github.com/casbin/casbin
docs:https://casbin.org/docs/zh-CN/overview
2.工作原理
在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。
PERM模型
PERM(Policy, Effect, Request, Matchers)模型很简单, 但是反映了权限的本质 – 访问控制
- Policy: 定义权限的规则
- Effect: 定义组合了多个 Policy 之后的结果, allow/deny
- Request: 访问请求, 也就是谁想操作什么
- Matcher: 判断 Request 是否满足 Policy
Module File 语法
casbin 是基于 PERM 的, 所有 model file 中主要就是定义 PERM 4 个部分
1.Request definition
[request_definition]
r = sub, obj, act
分别表示 request 中的
- accessing entity (Subject)
- accessed resource (Object)
- the access method (Action)
2.Policy definition
[policy_definition]
p = sub, obj, act
p2 = sub, act
定义的每一行称为 policy rule, p, p2 是 policy rule 的名字. p2 定义的是 sub 所有的资源都能执行 act
3.Policy effect
[policy_effect]
e = some(where (p.eft == allow))
上面表示有任意一条 policy rule 满足, 则最终结果为 allow
4.Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
定义了 request 和 policy 匹配的方式, p.eft 是 allow 还是 deny, 就是基于此来决定的
5.Role
[role_definition]
g = _, _
g2 = _, _
g3 = _, _, _
g, g2, g3 表示不同的 RBAC 体系, _, _ 表示用户和角色 _, _, _ 表示用户, 角色, 域
您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中获得RBAC角色和ABAC属性,并共享一组policy规则。
Casbin中最基本、最简单的model是ACL。ACL中的model CONF为:
# Request definition
[request_definition]
r = sub, obj, act
# Policy definition
[policy_definition]
p = sub, obj, act
# Policy effect
[policy_effect]
e = some(where (p.eft == allow))
# Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
ACL model的示例policy如下:
p, alice, data1, read
p, bob, data2, write
这表示:
- alice可以读取data1
- bob可以编写data2
Model存储
- 从 .CONF 文件中加载 model
e := casbin.NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
- 从代码加载 model
// Initialize the model from Go code.
m := casbin.NewModel()
m.AddDef("r", "r", "sub, obj, act")
m.AddDef("p", "p", "sub, obj, act")
m.AddDef("g", "g", "_, _")
m.AddDef("e", "e", "some(where (p.eft == allow))")
m.AddDef("m", "m", "g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act")
// Load the policy rules from the .CSV file adapter.
// 使用自己的 adapter 替换。
a := persist.NewFileAdapter("examples/rbac_policy.csv")
// 创建一个 enforcer。
e := casbin.NewEnforcer(m, a)
- 从字符串加载的 model(推荐)
// Initialize the model from a string.
text :=
`
[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 = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
`
m := NewModel(text)
// Load the policy rules from the .CSV file adapter.
// Replace it with your adapter to avoid files.
a := persist.NewFileAdapter("examples/rbac_policy.csv")
// Create the enforcer.
e := casbin.NewEnforcer(m, a)
这段代码是使用Casbin库定义了一个基本的权限访问控制模型。该模型包括了以下几个部分:
- 请求定义(r):定义了一个请求的三个要素,即主体(sub)、对象(obj)和动作(act)。
- 策略定义(p):定义了一个策略的三个要素,即主体(sub)、对象(obj)和动作(act)。
- 角色定义(g):定义了一种角色的概念,用于将主体与策略进行关联。
- 策略效果(e):定义了策略的生效效果,即只要有一条策略允许访问,则允许访问。
- 匹配器(m):定义了策略匹配的规则,即主体必须与策略的主体相同,对象必须满足keyMatch2规则,动作必须与策略的动作相同。
通过使用这个模型,可以根据主体、对象和动作来决定是否允许访问某个资源。具体的策略可以通过添加相应的规则来定义。
keyMatch规则
keyMatch是Casbin库中的一种匹配规则,用于匹配对象(obj)是否符合策略中的对象(p.obj)。
keyMatch规则支持两种通配符:
*
:匹配任意长度的字符串,但不包括路径分隔符/
。**
:匹配任意长度的字符串,包括路径分隔符/
。
keyMatch规则的匹配逻辑如下:
- 如果p.obj和r.obj相等,则匹配成功。
- 如果p.obj是以
*
结尾的字符串,且r.obj以p.obj去掉最后一个*
后缀的字符串开头,则匹配成功。 - 如果p.obj是以
**
结尾的字符串,且r.obj包含p.obj去掉最后一个**
后缀的字符串,则匹配成功。
举例来说,假设策略中的对象为/data/*
,则以下对象都会匹配成功:
/data/file1
/data/dir/file2
/data/dir/subdir/file3
而以下对象则不会匹配成功:
/data
/datafile1
/datafile2
通过使用keyMatch规则,可以灵活地定义对象匹配的策略,从而实现更精细的权限控制。
加载策略
要加载策略(p),你需要使用Casbin库提供的适配器(Adapter)来读取策略数据并将其加载到Casbin的模型(Model)中。
适配器(Adapter)负责将策略存储在不同的持久化存储中(如数据库、文件等),并提供读取和写入策略的接口。Casbin库提供了一些内置的适配器,如FileAdapter、MySQLAdapter等,你可以根据实际情况选择适合的适配器。
以下是一个加载策略的示例代码:
from casbin import CasbinEnforcer
from casbin.adapters import FileAdapter
# 创建一个CasbinEnforcer对象,并指定模型文件和策略适配器
enforcer = CasbinEnforcer('path/to/model.conf', 'path/to/policy.csv')
# 创建一个FileAdapter对象,并将其加载到CasbinEnforcer中
adapter = FileAdapter('path/to/policy.csv')
enforcer.set_adapter(adapter)
# 从适配器中加载策略到模型中
enforcer.load_policy()
# 现在策略已经加载到了模型中,可以进行权限访问控制了
在上述示例中,我们首先创建了一个CasbinEnforcer对象,并指定了模型文件和策略文件的路径。然后,我们创建一个FileAdapter对象,并将其加载到CasbinEnforcer中。最后,通过调用enforcer.load_policy()
方法,从适配器中加载策略数据到模型中。
根据实际情况,你可以选择使用不同的适配器和加载策略的方式。请根据你的具体需求和环境进行相应的调整。
Casbin API
//全局变量 e是执行者实例
e := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
//获取当前策略中显示的主题列表
allSubjects := e.GetAllSubjects()
//获取当前命名策略中显示的主题列表
allNamedSubjects := e.GetAllNamedSubjects("p")
//获取当前策略中显示的对象列表
allObjects := e.GetAllObjects()
//获取当前命名策略中显示的对象列表
allNamedObjects := e.GetAllNamedObjects("p")
//获取当前策略中显示的操作列表
allActions := e.GetAllActions()
//获取当前命名策略中显示的操作列表
allNamedActions := e.GetAllNamedActions("p")
//获取当前策略中显示的角色列表
allRoles = e.GetAllRoles()
//获取当前命名策略中显示的角色列表
allNamedRoles := e.GetAllNamedRoles("g")
//获取策略中的所有授权规则
policy = e.GetPolicy()
//获取策略中的所有授权规则,可以指定字段筛选器
filteredPolicy := e.GetFilteredPolicy(0, "alice")
//获取命名策略中的所有授权规则
namedPolicy := e.GetNamedPolicy("p")
//获取命名策略中的所有授权规则,可以指定字段过滤器
filteredNamedPolicy = e.GetFilteredNamedPolicy("p", 0, "bob")
//获取策略中的所有角色继承规则
groupingPolicy := e.GetGroupingPolicy()
//获取策略中的所有角色继承规则,可以指定字段筛选器
filteredGroupingPolicy := e.GetFilteredGroupingPolicy(0, "alice")
//获取策略中的所有角色继承规则
namedGroupingPolicy := e.GetNamedGroupingPolicy("g")
//获取策略中的所有角色继承规则
namedGroupingPolicy := e.GetFilteredNamedGroupingPolicy("g", 0, "alice")
// 确定是否存在授权规则
hasPolicy := e.HasPolicy("data2_admin", "data2", "read")
//确定是否存在命名授权规则
hasNamedPolicy := e.HasNamedPolicy("p", "data2_admin", "data2", "read")
//向当前策略添加授权规则。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddPolicy("eve", "data3", "read")
// 向当前命名策略添加授权规则。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddNamedPolicy("p", "eve", "data3", "read")
// 从当前策略中删除授权规则
removed := e.RemovePolicy("alice", "data1", "read")
// 移除当前策略中的授权规则,可以指定字段筛选器。 RemovePolicy 从当前策略中删除授权规则
removed := e.RemoveFilteredPolicy(0, "alice", "data1", "read")
//从当前命名策略中删除授权规则
removed := e.RemoveNamedPolicy("p", "alice", "data1", "read")
//从当前命名策略中移除授权规则,可以指定字段筛选器
removed := e.RemoveFilteredNamedPolicy("p", 0, "alice", "data1", "read")
//确定是否存在角色继承规则
has := e.HasGroupingPolicy("alice", "data2_admin")
//确定是否存在命名角色继承规则
has := e.HasNamedGroupingPolicy("g", "alice", "data2_admin")
// 向当前策略添加角色继承规则。 如果规则已经存在,函数返回false,并且不会添加规则。 如果规则已经存在,函数返回false,并且不会添加规则
added := e.AddGroupingPolicy("group1", "data2_admin")
//将命名角色继承规则添加到当前策略。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddNamedGroupingPolicy("g", "group1", "data2_admin")
// 从当前策略中删除角色继承规则
removed := e.RemoveGroupingPolicy("alice", "data2_admin")
//从当前策略中移除角色继承规则,可以指定字段筛选器
removed := e.RemoveFilteredGroupingPolicy(0, "alice")
//从当前命名策略中移除角色继承规则
removed := e.RemoveNamedGroupingPolicy("g", "alice")
//当前命名策略中移除角色继承规则,可以指定字段筛选器
removed := e.RemoveFilteredNamedGroupingPolicy("g", 0, "alice")
//添加自定义函数
func CustomFunction(key1 string, key2 string) bool {
if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data/:resource" {
return true
} else if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data2/:id/using/:resId" {
return true
} else {
return false
}
}
func CustomFunctionWrapper(args ...interface{}) (interface{}, error) {
key1 := args[0].(string)
key2 := args[1].(string)
return bool(CustomFunction(key1, key2)), nil
}
e.AddFunction("keyMatchCustom", CustomFunctionWrapper)
评论区