访问控制是指区分不同用户对不同资源的访问能力,在应用系统中通常叫做权限管理
在做任何多用户系统的时候,用户权限管理都是整个系统中必不可少的部分,并且权限设计要做到既安全又清晰是一件不太容易的事情。
根据不同的场景,权限管理可以设计出各种不同的模型。
今天就给各位介绍一个访问控制框架Casbin以及PERM元模型。
Casbin · An authorization library
元模型 PERM
如果,在做系统设计时,针对每一个资源和每一个用户都编写访问规则将会是一个工作量很大的事情,并且对于动态变化的资源和用户,这样做是不可行的。因此我们需要定义一系列规则,这些规则的组合就是一个访问控制模型。
PERM模型是由4个基础(Policy,Effect,Request,Matchers)描述各个资源和用户之间的相互关系。
Request 请求
定义了请求参数。一个基本的请求是一个元组对象,至少包含subject(访问实体), object(访问的资源)和 action(访问方法)。
r={sub, obj,act}
它其实就是定义了传入访问控制匹配函数的参数名和顺序。
Policy 策略
定义访问策略的模型。其实就是定义Policy规则文档中各字段的名称和顺序。
p={sub, obj, act}
或 p={sub, obj, act, eft}
注意:如果不定义 eft(策略结果),那么将不会去读策略文件中的结果字段,并将匹配的策略结果都默认为allow
。
Matchers 匹配规则
Request和Policy的匹配规则。
例如: m = r.sub == p.sub && r.act == p.act && r.obj == p.obj
这条简单又常见的匹配规则的意思就是,请求的参数(实体、资源和方法)都相等即在策略中能找到,那么返回策略结果(p.eft)。策略结果会保存在p.eft
中。
Effect
用于将给定请求与最终结果匹配的策略组合/减少的策略的模型。可以理解为,对Matchers匹配后的结果再进行一次逻辑组合判断的模型。
例如:e = some(where(p.eft == allow))
这句的意思是指,如果匹配策略结果p.eft
存在(some
) allow
的结果,那么最终结果就为 真
再看个例子:
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
这个例子组合的逻辑含义是:如果有匹配出结果为alllow
的策略并且没有匹配出结果为deny
的策略则结果为真,换句话说,就是匹配的策略都为allow
时才为真,如果有任何deny
,都为假 (更简单的说当allow
和deny
同时存在时,deny
优先)
以上时PERM的模型定义的说明,接下来,用Casbin实战一下
Casbin访问控制需要两个东西:访问控制模型文件和策略文件,模型文件就是上文描述的内容。
下图说明了如何使用基于 PERM 的模型授权请求。
创建一个完整的模型文件
创建一个perm.conf文件,内容如下
# Request definition
[request_definition]
r = sub, obj, act
# Policy definition
[policy_definition]
p = sub, obj, act, eft # 这里我们定义了eft,不使用默认值
# Policy effect
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) # 这里使用了deny优先
# Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act #最简单的匹配规则。请求的参数与策略参数一致时获得策略结果
注意代码中的注释
创建一个policy.csv文件, 其中的每个字段的定义就如perm中[policy_definition]中定义的顺序:
p, zeta, data1, read, allow
p, bob, data2, write, allow
p, zeta, data2, write, deny
p, zeta, data2, write, allow
p 代表策略组名称,就是metchers里对应的那个p。后面的zeta,data, read, allow对应的就是策略定义中的sub,obj,act和策略结果eft。
假如,request参数在策略文件中能够匹配(zeta,data1和read),那么结果为allow;
假如,request参数在策略文件中能够匹配(zeta,data2,write),那么结果为deny,同时也匹配另一条策略结果为allow,根据policy_effect的定义,依然识别为假。
接下来用Go代码检验一下
创建main.go 代码如下:
package main
import (
“fmt”
“github.com/casbin/casbin”
)
func main() {
//通过策略文件和模型配置穿件一个casbin访问控制实例
e := casbin.NewEnforcer(“./perm.conf”, “./policy.csv”)
//定义各种sub,obj和act的数组
subs := []string{“bob”, “zeta”}
objs := []string{“data1”, “data2”}
acts := []string{“read”, “write”}
//遍历组合sub,obj,act并打印出对应策略匹配结果。
for _, sub := range subs {
for _, obj := range objs {
for _, act := range acts {
fmt.Println(sub, “/“, obj, “/“, act, “=“, e.Enforce(sub, obj, act))
}
}
}
}
这段Go代码很简单,组合每一种sub、obj和act,然后打印出访问控制的结果验证我们的策略文件和模型的设计。
运行结果为:
bob / data1 / read = false
bob / data1 / write = false
bob / data2 / read = false
bob / data2 / write = true
zeta / data1 / read = true
zeta / data1 / write = false
zeta / data2 / read = false
zeta / data2 / write = false
bob的策略很简单,只有data2的write结果为allow,因此其余都是false,结果正确;
zeta对data1的read结果为allow,对data2的write同时有allow和denny(根绝policy_effect定义的deny优先),因此结果zeta也只对 data1的read 为true,结果正确。
这样,我们就使用Casbin和PERM创建了一个基础的访问控制模型设计和策略。
在实际应用上,如果我们做的是一个Web应用,可以将路由作为obj,请求方式Method作为act,登录用户的角色作为sub,在每一次请求时,把这3个参数传递给e.Enforce
, 就可以实现对Web页面和请求接口的权限控制管理,非常方便。
高级
存储
Casbin除了能够通过策略文件和模型配置文件设置访问控制外,还可以适配数据库模式,将策略和模型保存在数据库中,更适合大型复杂的软件系统的权限管理。
model存储
policy存储
函数
文中的模型配置没有使用额外的函数,但是在实际应用中,对与资源的匹配模式,action的匹配模式可能会用到更高级的匹配方法。利用函数是最佳实践方式。
Policy存储 · Casbin
API
Casbin提供给了很多API用于管理 管理 API · Casbin