基於casbin的RBAC許可權實踐

image

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

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

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

  • 許可權的數據源可以切換,但是邏輯基本不用動
  • 許可權策略定義簡單不複雜,支援RBAC,ABAC(粒度可粗可細)
  • 支援內置超級用戶(上帝模式)

知道我最近研究了一下casbin(基於各種訪問控制模型的授權), 發現它正好滿足了我以上幾個點 官網: //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是非常簡單的,這裡主要分享了結合具體項目來如何設計,細節源碼可以查看

//github.com/yuzd/Spa

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