基於casbin的RBAC許可權實踐

俞正東發表於2022-05-03
image

五一假期疫情封在家也沒事做,就想來優化一下一個前端容器小專案

之前的TODOlist裡面有一項是許可權這塊時隔2年了還一直沒有動手

遲遲沒搞主要還是我太懶了,哈哈 其實我一直想要找一個輕量級的許可權通用方案

  • 許可權的資料來源可以切換,但是邏輯基本不用動
  • 許可權策略定義簡單不復雜,支援RBAC,ABAC(粒度可粗可細)
  • 支援內建超級使用者(上帝模式)

知道我最近研究了一下casbin(基於各種訪問控制模型的授權), 發現它正好滿足了我以上幾個點 官網: https://casbin.org/

基於cashbin的許可權實踐

1. 許可權設計

分為2種許可權:超級管理員(上帝模式) 和 普通使用者

我這個程式的功能是按照專案維度來區分的,超級管理員建立一個空專案後,授權給別人去維護,總共包含7大功能:

超級管理員可以訪問所有功能, 但只能是【超級管理員】做的有1和2和3

  • 1.許可權配置(普通使用者建立和刪除,許可權的修改和儲存)
  • 2.全域性配置
  • 3.建立空專案
  • 4.上傳並部署該專案
  • 5.把專案回滾到上一個版本
  • 6.專案維度的服務端js指令碼(讀和寫)
  • 7.專案維度的配置檔案(讀和寫)

普通使用者則可以被超級管理員在許可權配置頁面建立並進行配置來限制是否授予訪問4~7這幾個功能

2. 程式碼開發

casbin基本主流的開發語言都有對應的實現,這裡我用netcore版本(Casbin.NET)

首先定義模型:


[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

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

[matchers]
r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) || r.sub == "root"

由於我這個是按照project進行許可權控制的,所以我選用的是這個模型

  • sub -> user(登入使用者名稱,root是超級使用者/上帝模式)
  • obj -> project(專案)
  • act -> api資源(這裡用了基於正則的方式為了應對配置一個使用者可以訪問project下所有許可權)

/// <summary>
/// 建立casbin模型
/// </summary>
/// <returns></returns>
public static Enforcer createEnforcer()
{
    var e = new Enforcer();
    var m = NetCasbin.Model.Model.CreateDefault();
    m.AddDef("r", "r", "sub, obj, act");
    m.AddDef("p", "p", "sub, obj, act");
    m.AddDef("e", "e", "some(where (p.eft == allow))");
    m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) || r.sub == "root"");
    var csv = Path.Combine(WebRootPath, CasBinPolicyFile);
    if (!File.Exists(csv))
    {
        File.CreateText(csv);
    }

    e.SetModel(m);
    // 目前我的許可權配置檔案是放在csv檔案中 切換成存db的話 就切換一個adapter
    e.SetAdapter(new DefaultFileAdapter(csv));
    e.LoadPolicy();
    return e;
}

由於本身我的這個專案是一箇中介軟體,

image

//內部api
app.UseWhen(
    c =>
    {
        // 檢查路由是否滿足要求
        if (!ApiMiddleware.CanInvoke(c, out var route))
        {
            return false;
        }
        // 路由規則滿足後檢查api是否存在
        return c.RequestServices.GetService<SpaDomain>()?.IsSpaApi(route.Item2) ?? false;
    },
    _ => _.UseMiddleware<ApiMiddleware>());

對於普通使用者可訪問的內部的api訪問路徑進行規則約束

  • 4.上傳並部署該專案-> /{project}.reupload
  • 5.把專案回滾到上一個版本 -> /{project}.rollback
  • 6.專案維度的服務端js指令碼(讀) -> /{project}.getconfigjson
  • 專案維度的服務端js指令碼(寫) -> /{project}.saveconfigjson
  • 7.專案維度的配置檔案(讀) -> /{project}.serverjsget
  • 專案維度的配置檔案(寫)-> /{project}.serverjssave

這樣我在ApiMiddleware裡面可以進行統一許可權攔截處理了

  • 解析請求路徑 拿到 project(obj) 和 act (api)
  • 拿到當前登入 拿到 sub(user)
  • 拿到了sub,obj,act三要素後呼叫casbin方法進行驗證
bool isAuthed = ef.Enforce(sub, obj, act);
image

設計一個頁面來去配置策略

image

這也是針對casbin的一個ui操作的封裝

  • 支援建立使用者
  • casbin的策略進行增刪改查
  • 支援的api資源的列表展示

那麼通過這個ui操作就很容易去配置

粗粒度:某個使用者對哪些project有許可權
image

如上圖,資源路徑我配置了/* 代表這個zdyu使用者可以訪問project:test的所有操作

細力度:某個使用者對哪些project的哪些具體操作有許可權
image

如上圖,代表zdyu這個使用者只荀彧訪問test這個project下的 部署和回滾2個功能

總結

本身研究怎麼用casbin是非常簡單的,這裡主要分享了結合具體專案來如何設計,細節原始碼可以檢視

https://github.com/yuzd/Spa

spa單頁面容器裡面一個project相當於一個二級域名的應用,應用內互相隔離,可以代替部署nginx或apache,對前端開發者友好,適合在某些只是用來靜態專案訪問的場景,來提高效率!

 

基於casbin的RBAC許可權實踐

相關文章