基於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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章