工作原理
在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中结合RBAC角色和ABAC属性,并共享一组policy规则。
PERM模式由四个基础(政策、效果、请求、匹配)组成,描述了资源与用户之间的关系。
请求
请求是一个元组对象,定义了应该提供访问控制匹配功能的参数名称和顺序。
至少由三部分组成:主题(访问实体)、对象(访问资源)、动作(访问方式)。
一个请求形如:r = {sub,obj,act}
策略
定义访问策略模式,是在策略规则文件中定义的。
例如:p={sub,obj,act}或者p={sub,obj,act,eft}。如果没有定义eft,那么策略文件中的结果字段就不会被读取,和匹配的策略结果将默认被允许。
匹配器
匹配请求和策略的规则。定义根据请求找到策略的方式。
例如:m = r.sub == p.sub && r.act == p.act && r.obj == p.obj,这个匹配规则表示如果请求的参数匹配策略中的资源和方法,那么就返回策略结果p.eft。
效果
可以被理解为一种模型,在这种模型中,对匹配的结果再次做出逻辑组合判断。 因为一个请求根据匹配器最终找到的策略可能不只一个。
例如:e = some(where(p.eft==allow))表示如果匹配的策略结果由一些是允许的,那么最终结果为真。
还有一个示例:e = some(where(p.eft==allow)) && !some(where(p.eft==deny))表示有匹配的allow的策略并且不存在有匹配deny的策略那么结果就为真。deny的优先级要高于allow。
ACL示例
casbin支持的最简单的访问控制模型为ACL,那么就以一个完整的ACL模型为例。
首先是model文件中的配置,对上面的四部分进行了定义。
# 请求定义
[requet_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这里定义了请求是由三部分组成的,策略也是由三部分组成的。请求和策略的匹配方式是两者的主题、对象、动作都一样。最终的效果是存在allow的策略结果结果就为真。
casbin中匹配器额定义可以使用\来支持多行模式。对于golang,还支持in操作,例如m = r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3'),但是数组的长度必须大于1,否则会panic。
`
然后在策略文件中编写具体的策略,ACL的示例策略为:
p, alice, data1, read
p, bob, data2, write
表示,alice可以读取data1,bob可以写data2。
匹配器中的函数
如何将请求与策略进行匹配是一个重点,为了便于灵活指定匹配规则,可以使用自定义的函数,也可以使用内置函数来辅助。下面介绍一下支持的内置函数。
除了keyGet和keyGet2,所有的内置函数的格式都要为
func function_name(arg1, arg2 string) bool作用都是一样的,即arg1是否匹配arg2。而keyGet和keyGet2是返回匹配通配符的字符串,如果没有则返回空字符串。
内置函数
| 函数 | 参数1 | 参数2 |
|---|---|---|
| keyMatch | 一个URL路径,例如/alice_data/resource1 | 一个URL路径或*模式下,例如/alice_data/* |
| keyGet | 一个URL路径,例如/alice_data/resource1 | 一个URL路径或*模式下,例如/alice_data/* |
| keyMatch2 | 一个URL路径,例如/alice_data/resource1 | 一个URL路径或:模式下,例如/alice_data/:resource |
| keyGet2 | 一个URL路径,例如/alice_data/resource1 | 一个URL路径或:模式下,例如/alice_data/:resource |
| keyMatch3 | 一个URL路径,例如/alice_data/resource1 | 一个URL路径或{}模式下,例如/alice_data/{resource} |
| keyMatch4 | 一个URL路径,例如/alice_data/resource1 | 一个URL路径或{}模式下,例如/alice_data//{id}/book/{id} |
| regexMatch | 任意字符串 | 正则表达式模式 |
| ipMatch | 一个IP地址,例如192.168.2.123 | 一个 IP 地址或一个 CIDR ,例如192.168.2.0/24 |
| globMatch | 类似路径的 /alice_data/resource1 | 一个全局模式,例如 /alice_data/* |
自定义函数
首先准备好自定义的函数,它接收一些参数并返回bool类型。
func KeyMatch(key1 string, key2 string) bool {
i := strings.Index(key2, "*")
if i == -1 {
return key1 == key2
}
if len(key1) > i {
return key1[:i] == key2[:i]
}
return key1 == key2[:i]
}然后用interface{}封装一下:
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return (bool)(KeyMatch(name1, name2)), nil
}在casbin的Enforcer中注册这个函数,
e.AddFunction("my_func", KeyMatchFunc)最后就可以在model.conf中使用这个函数了
[matchers]
m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act