1. 程式人生 > >casbin-權限管理

casbin-權限管理

sse 覆蓋 bin str 定義 技術分享 角色 access star

概要

權限管理幾乎是每個系統或者服務都會直接或者間接涉及的部分. 權限管理保障了資源(大部分時候就是數據)的安全, 權限管理一般都是和業務強關聯, 每當有新的業務或者業務變化時, 不能將精力完全放在業務實現上, 權限的調整往往耗費大量的精力.

其實, 權限的本質沒有那麽復雜, 只是對訪問的控制而已, 有一套完善的訪問控制接口, 再加上簡單的權限模型. 權限模型之所以能夠簡單, 就是因為權限管理本身並不復雜, 只是在和具體業務結合時, 出現了各種各樣的訪問控制場景, 才顯得復雜.

PERM 模型

PERM(Policy, Effect, Request, Matchers)模型很簡單, 但是反映了權限的本質 – 訪問控制

  • Policy: 定義權限的規則
  • Effect: 定義組合了多個 Policy 之後的結果, allow/deny
  • Request: 訪問請求, 也就是誰想操作什麽
  • Matcher: 判斷 Request 是否滿足 Policy

技術分享圖片

casbin 權限庫

casbin 使用了 PERM 模型來表達權限, 並且提供了簡單直接的 API.

核心概念

model file

用來定義具體的權限模型, 目前支持的模型基本覆蓋了常見的所有場景:

  1. ACL
  2. ACL with superuser
  3. ACL without users
  4. ACL without resources
  5. RBAC
  6. RBAC with resource roles
  7. RBAC with domains/tenants
  8. ABAC
  9. ……

model 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 體系, _, _ 表示用戶和角色 _, _, _ 表示用戶, 角色, 域(也就是租戶)

policy file

定義具體的策略, 權限的檢查就是基於定義的 model file 和 policy file 來完成的.

相對於 model file 定義規則, policy file 中定義的就是具體的內容.

RBAC 示例

定義 model file

[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 = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

定義 policy file

p, superAdmin, project, read
p, superAdmin, project, write
p, admin, project, read
p, admin, project, write
p, admin, asse, read
p, admin, asse, write
p, zhuangjia, project, write
p, zhuangjia, asse, write
p, shangshang, project, read
p, shangshang, asse, read

g, quyuan, admin
g, wenyin, zhuangjia

測試代碼

package rbac

import (
  "fmt"
  "log"

  "github.com/casbin/casbin"
)

func TestRBAC() {
  e := casbin.NewEnforcer("rbac/rbac.conf", "rbac/rbac.csv")

  fmt.Printf("RBAC test start\n") // output for debug

  // superAdmin
  if e.Enforce("superAdmin", "project", "read") {
    log.Println("superAdmin can read project")
  } else {
    log.Fatal("ERROR: superAdmin can not read project")
  }

  if e.Enforce("superAdmin", "project", "write") {
    log.Println("superAdmin can write project")
  } else {
    log.Fatal("ERROR: superAdmin can not write project")
  }

  // admin
  if e.Enforce("quyuan", "project", "read") {
    log.Println("quyuan can read project")
  } else {
    log.Fatal("ERROR: quyuan can not read project")
  }

  if e.Enforce("quyuan", "project", "write") {
    log.Println("quyuan can write project")
  } else {
    log.Fatal("ERROR: quyuan can not write project")
  }

  if e.Enforce("quyuan", "asse", "read") {
    log.Println("quyuan can read asse")
  } else {
    log.Fatal("ERROR: quyuan can not read asse")
  }

  if e.Enforce("quyuan", "asse", "write") {
    log.Println("quyuan can write asse")
  } else {
    log.Fatal("ERROR: quyuan can not write asse")
  }

  // zhuangjia
  if e.Enforce("wenyin", "project", "read") {
    log.Fatal("ERROR: wenyin can read project")
  } else {
    log.Println("wenyin can not read project")
  }

  if e.Enforce("wenyin", "project", "write") {
    log.Println("wenyin can write project")
  } else {
    log.Fatal("ERROR: wenyin can not write project")
  }

  if e.Enforce("wenyin", "asse", "read") {
    log.Fatal("ERROR: wenyin can read asse")
  } else {
    log.Println("wenyin can not read asse")
  }

  if e.Enforce("wenyin", "asse", "write") {
    log.Println("wenyin can write asse")
  } else {
    log.Fatal("ERROR: wenyin can not write asse")
  }

  // shangshang
  if e.Enforce("shangshang", "project", "read") {
    log.Println("shangshang can read project")
  } else {
    log.Fatal("ERROR: shangshang can not read project")
  }

  if e.Enforce("shangshang", "project", "write") {
    log.Fatal("ERROR: shangshang can write project")
  } else {
    log.Println("shangshang can not write project")
  }

  if e.Enforce("shangshang", "asse", "read") {
    log.Println("shangshang can read asse")
  } else {
    log.Fatal("ERROR: shangshang can not read asse")
  }

  if e.Enforce("shangshang", "asse", "write") {
    log.Fatal("ERROR: shangshang can write asse")
  } else {
    log.Println("shangshang can not write asse")
  }
}

多租戶示例

定義 model file

[request_definition]
r = sub, dom, obj, act

[policy_definition]
p = sub, dom, obj, act

[role_definition]
g = _, _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act

定義 policy file

p, superAdmin, gy, project, read
p, superAdmin, gy, project, write
p, superAdmin, jn, project, read
p, superAdmin, jn, project, write
p, admin, gy, project, read
p, admin, gy, project, write
p, admin, jn, asse, read
p, admin, jn, asse, write
p, zhuangjia, jn, project, write
p, zhuangjia, gy, asse, write

g, quyuan, admin, gy
g, quyuan, admin, jn
g, wenyin, zhuangjia, gy
g, shangshang, zhuangjia, jn

測試代碼

package tenants

import (
  "fmt"
  "log"

  "github.com/casbin/casbin"
)

// TestTenants test tenants
func TestTenants() {
  e := casbin.NewEnforcer("tenants/tenants.conf", "tenants/tenants.csv")

  fmt.Printf("RBAC TENANTS test start\n") // output for debug

  // superAdmin
  if e.Enforce("superAdmin", "gy", "project", "read") {
    log.Println("superAdmin can read project in gy")
  } else {
    log.Fatal("ERROR: superAdmin can not read project in gy")
  }

  if e.Enforce("superAdmin", "gy", "project", "write") {
    log.Println("superAdmin can write project in gy")
  } else {
    log.Fatal("ERROR: superAdmin can not write project in gy")
  }

  if e.Enforce("superAdmin", "jn", "project", "read") {
    log.Println("superAdmin can read project in jn")
  } else {
    log.Fatal("ERROR: superAdmin can not read project in jn")
  }

  if e.Enforce("superAdmin", "jn", "project", "write") {
    log.Println("superAdmin can write project in jn")
  } else {
    log.Fatal("ERROR: superAdmin can not write project in jn")
  }

  // admin
  if e.Enforce("quyuan", "gy", "project", "read") {
    log.Println("quyuan can read project in gy")
  } else {
    log.Fatal("ERROR: quyuan can not read project in gy")
  }

  if e.Enforce("quyuan", "gy", "project", "write") {
    log.Println("quyuan can write project in gy")
  } else {
    log.Fatal("ERROR: quyuan can not write project in gy")
  }

  if e.Enforce("quyuan", "jn", "project", "read") {
    log.Fatal("ERROR: quyuan can read project in jn")
  } else {
    log.Println("quyuan can not read project in jn")
  }

  if e.Enforce("quyuan", "jn", "project", "write") {
    log.Fatal("ERROR: quyuan can write project in jn")
  } else {
    log.Println("quyuan can not write project in jn")
  }

  if e.Enforce("quyuan", "gy", "asse", "read") {
    log.Fatal("ERROR: quyuan can read asse in gy")
  } else {
    log.Println("quyuan can not read asse in gy")
  }

  if e.Enforce("quyuan", "gy", "asse", "write") {
    log.Fatal("ERROR: quyuan can write asse in gy")
  } else {
    log.Println("quyuan can not write asse in gy")
  }

  if e.Enforce("quyuan", "jn", "asse", "read") {
    log.Println("quyuan can read asse in jn")
  } else {
    log.Fatal("ERROR: quyuan can not read asse in jn")
  }

  if e.Enforce("quyuan", "jn", "asse", "write") {
    log.Println("quyuan can write asse in jn")
  } else {
    log.Fatal("ERROR: quyuan can not write asse in jn")
  }

  // wenyin
  if e.Enforce("wenyin", "gy", "asse", "write") {
    log.Println("wenyin can write asse in gy")
  } else {
    log.Fatal("ERROR: wenyin can not write asse in gy")
  }

  if e.Enforce("wenyin", "jn", "asse", "write") {
    log.Fatal("ERROR: wenyin can write asse in jn")
  } else {
    log.Println("wenyin can not write asse in jn")
  }

  // shangshang
  if e.Enforce("shangshang", "jn", "project", "write") {
    log.Println("shangshang can write project in jn")
  } else {
    log.Fatal("ERROR: shangshang can not write project in jn")
  }

  if e.Enforce("shangshang", "gy", "project", "write") {
    log.Fatal("ERROR: shangshang can write project in gy")
  } else {
    log.Println("shangshang can not write project in gy")
  }
}

總結

casbin 權限管理庫比較簡單, 易上手, 但是它的功能卻不簡單, 支持了目前主流的所有權限管理場景. 在使用上, model file 和 poclicy file 的定義也簡單明了, 抽象出了權限管理最本質的東西.

將具體業務中的權限要求映射到 casbin 中 model file, 就可以借助 casbin 的 API, 快速的實現權限管理.

casbin-權限管理