基于Casbin实现ABAC
最近同事在研究Casbin的权限设计,我们主要是考虑使用ABAC基于属性的访问控制,Casbin给的示例不多,于是自己写了几个示例。
首先我们看看提到ABAC时,一般描述如下:
ABAC被一些人称为是权限系统设计的未来。
不同于常见的将用户通过某种方式关联到权限的方式,ABAC则是通过动态计算一个或一组属性来是否满足某种条件来进行授权判断(可以编写简单的逻辑)。属性通常来说分为四类:用户属性(如用户年龄),环境属性(如当前时间),操作属性(如读取)和对象属性(如一篇文章,又称资源属性),所以理论上能够实现非常灵活的权限控制,几乎能满足所有类型的需求。
例如规则:“允许所有班主任在上课时间自由进出校门”这条规则,其中,“班主任”是用户的角色属性,“上课时间”是环境属性,“进出”是操作属性,而“校门”就是对象属性了。为了实现便捷的规则设置和规则判断执行,ABAC通常有配置文件(XML、YAML等)或DSL配合规则解析引擎使用。
这里我们就以这个班主任上课进出校门为例,看看在Casbin下是如何实现的:
首先,我们定义用户环境和对象,操作我们就直接用字符串
type Person struct{ Role string Name string } type Gate struct{ Name string } type Env struct{ Time time.Time Location string } func (env *Env) IsSchooltime() bool{ return env.Time.Hour()>=8&&env.Time.Hour()<=18 }
接下来我们根据这个权限描述,我们可以写出如下的Casbin PERM模板:
[request_definition] r = sub, obj, act, env [policy_definition] p = sub, obj,act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub.Role=='Teacher' && r.obj.Name=='School Gate' && r.act in('In','Out') && r.env.Time.Hour >=8 && r.env.Time.Hour <= 18
因为我们给Env对象定义了IsSchooltime方法,所以我们也可以把目标写成如下,也是一样的效果:
[request_definition] r = sub, obj, act, env [policy_definition] p = sub, obj,act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub.Role=='Teacher' && r.obj.Name=='School Gate' && r.act in('In','Out') && r.env.IsSchooltime()
接下来我们构造两个人,一个是学生Yun,一个是老师Devin,构造两个门,一个是工厂大门,一个是学校大门,操作的方法我们就定义进门In和控制大门Control两个操作,环境上我们定义一个是早上9点,一个是晚上23点。
完整代码如下:
func TestTeacherEnterSchoolGate() { p1 := Person{Role: "Student", Name: "Yun"} p2 := Person{Role: "Teacher", Name: "Devin"} persons := []Person{p1, p2} g1 := Gate{Name: "School Gate"} g2 := Gate{Name: "Factory Gate"} gates := []Gate{g1, g2} const modelText = ` [request_definition] r = sub, obj, act, env [policy_definition] p = sub, obj,act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub.Role=='Teacher' && r.obj.Name=='School Gate' && r.act in('In','Out') && r.env.Time.Hour >7 && r.env.Time.Hour <= 18 ` //m = r.sub.Role=='Teacher' && r.obj.Name=='School Gate' && r.act in('In','Out') && r.env.IsSchooltime() m := model.Model{} m.LoadModelFromText(modelText) e := casbin.NewEnforcer(m) envs := []*Env{InitEnv(9), InitEnv(23)} for _, env := range envs { fmt.Println("\r\nTime:",env.Time.Local()) for _, p := range persons { for _, g := range gates { pass := e.Enforce(p, g, "In", env) fmt.Println(p.Role, p.Name, "In", g.Name, pass) pass = e.Enforce(p, g, "Control", env) fmt.Println(p.Role,p.Name, "Control", g.Name, pass) } } } } func InitEnv(hour int) *Env{ env:=&Env{} env.Time=time.Date(2019,8,20,hour,0,0,0,time.Local) return env }
最后,输出结果如下:
Time: 2019-08-20 09:00:00 +0800 CST
Student Yun In School Gate false
Student Yun Control School Gate false
Student Yun In Factory Gate false
Student Yun Control Factory Gate false
Teacher Devin In School Gate true
Teacher Devin Control School Gate false
Teacher Devin In Factory Gate false
Teacher Devin Control Factory Gate false
Time: 2019-08-20 23:00:00 +0800 CST
Student Yun In School Gate false
Student Yun Control School Gate false
Student Yun In Factory Gate false
Student Yun Control Factory Gate false
Teacher Devin In School Gate false
Teacher Devin Control School Gate false
Teacher Devin In Factory Gate false
Teacher Devin Control Factory Gate false
我们可以看到,在上课时间早上9点,学生是禁止进出校门,而只有老师Devin的进校门操作被通过。而到了晚上23点,老师Devin也不允许进校门了。
这里需要注意的是,在一般的模板中,是没有env这个环境变量的,我把它加到了request_definition的最后面写成r = sub, obj, act, env,同时e.Enforce(sub,obj,act,env)需要按顺序传入4个参数。
我还会对Casbin的ABAC进一步的研究,各测试用例会发布到https://github.com/studyzy/abactest 有兴趣的可以看看。
最后,关于Casbin采用的规则引擎为,https://github.com/Knetic/govaluate,编辑Matchers规则可以参考:https://github.com/Knetic/govaluate/blob/master/MANUAL.md