基於 mongodb 設計靈活後台系統管理權限

來源:互聯網
上載者:User

基於 mongodb 設計靈活後台系統管理權限

mongodb 是一款基於 文檔 結構的 nosql 資料庫,目前社區比較火,在文檔的儲存靈活性有著天然的優勢(同集合可以任何形式的行資料,當然我們不會這樣去爛用這種靈活性 :) ),且有不俗的效能表現,以及複本集的高可用!

此款許可權粒度存在範圍與個體的概念,可以理解為 功能性許可權 和 資料歸屬性許可權。資料許可權是功能許可權的再次細分,作為子集。下面,我會一點點剖析,下面的案例會給出所有表結構,和關鍵操作的 sql 語句(基於 golang 語言的 (mgo mongodb driver))和設計圖。當然這可能並不是最佳實務,只是我自己設計的一點心得,已便解決實際問題。

背景

之前在做一款早期 serverless 架構的雛形(現在只能算模版系統 :) )後台管理系統的時候,公司的人員結構很穩定,又為了快速內部上線提供使用,我們想到一款最簡單的基於部門實現泛許可權的功能許可權管理,意思就是,這個部門下面的組擁有這個組建立的所有查看許可權,這個組擁有的功能許可權由角色定義,如果為資深開發人員角色,那麼管理員會分配 模版編輯許可權,模版清緩衝/發布等一套可以從代碼web ide 上編輯到外網訪問發布的整個流程許可權,初級可能只有編輯許可權等。

但是我們很快發現問題,公司雖然有統一的許可權系統,但是控制粒度太粗,所以開始我們是把人員的許可權放入到自己的庫裡維護,時隔幾月,隨著公司的大舉擴張,人員結構,部門等發生翻天覆地的變化,人事變更意味著之前基於部門設計的許可權變成了髒資料,部門之間的資料沒法自動實現訪問,只能通過我們簡單的資料許可權操作機制,讓某一人擁有多個部門的許可權,這樣處理因為不方便的原因可能存在越權處理,因為在 A 部門你擁有發布許可權, 在 B 部門我只想讓你有查詢功能等,這樣有一段時間我們就陷入了不斷為人調整許可權的售後工作!而且在跨部門處理問題都會面臨不方便。

結構圖

思考

當時我們面臨一個元素種類比較多的後台系統,且許可權組合種類比較多,那麼怎樣能夠設計一款能夠自由組合,能夠自驅玩轉許可權,許可權的下方不再由單人控制,以及在做工作交接的時候許可權與資源能夠一鍵交接。所以列了如下幾點特性。

  1. 許可權控制下放,開發人員不再費力維護許可權。
  2. 許可權有角色管理控制,有資料許可權的細分概念。
  3. 許可權以及資源能夠一鍵轉讓。
  4. 許可權基於人為個體,細分到,某個人下的某條資料。
  5. 許可權可以臨時組成,不同開發人員,可以同時擁有一套或多套許可權,方便跨部門協作。
  6. 使用要簡單,與使用者基本資料隔離
  7. 效能要出眾。可以做成 web 服務 / sdk

實踐

接下來就是進行表結構的梳理,所有結構都會給出 最簡潔關鍵字段及註解

要想細分到資料許可權,那麼人/組必須要有一個元素與資源進行綁定,在此我們用 signkey 作為一種簽名,某個人擁有這個簽名就有對這個資源有一定的操作許可權。一定要有中間簽名的紐帶來建立聯絡,不然資料量的儲存,轉讓,變動都會出現問題。 如果說把資源屬於哪些人來處理的話,可能一個欄位就有很多工號來識別。

所以我們許可權表裡面的 user 資訊表這樣設計

type UserDoc struct {    Name      string          // 姓名    UserId    string          // 工號 唯一標識    SignKey   map[string]string // 簽名 key是簽名 24字元id mongodb _id (可換成任意 唯一 字串),value是簽名的描述。}

可以看到一個人自己可以建立多個私人 簽名
GroupName+UserId 作聯合唯一索引

那麼我們的角色許可權是怎麼設計的呢?一個角色擁有多種職能,我們把每一種職能看作一個介面實現了不同的方法,那麼角色許可權,就是由一個或多個介面實現的組合,介面實現又體現在 url + method 上面,然而問題是,有些介面涉及到操作資料,對資料有寫操作,有的介面,只是單純的某一次事件,不涉及到任何資料查詢和變更。那麼在這裡就會劃分此介面是否開啟對資料做認證。通過不同的介面實現組合,這樣就擁有了角色許可權的維護。

type RoleDoc struct {    RoleName        string          // 角色名稱    Desc            string          // 角色描述    IsDefault       bool            // 是否為預設角色,就是使用者登陸進來內建的角色許可權    UserIds         []string        // 角色下面有哪些使用者,  這裡是一個最佳化點,可以使用外鍵表關聯。 視系統大小可以調整    PathsDataVerify map[string]bool // 該介面是否開啟資料驗證許可權,用於 sql 快速查詢資料    PathMethods     []PathMethod    // 該角色有哪些功能即 API 的組合, 詳細資料,是一份冗餘資料,方便配置時使用 通過方法把詳細資料轉化為快速查詢資料    Typ             int             // 角色類型,用於做特權處理, 例如 超級管理員角色,無需驗證許可權 則 type = 0}// restful API 風格設計 一個 path 有多個 method,// method 使用 int 代替 GET=1 POST=2 PUT=4 DELETE=8 HEAD=16... 可以用到 mongodb 位元運算// 如果一個 path 的 get  post 方法都賦予了這個角色, 則 MethodInt=3type PathMethod struct {    Path      string // 請求路徑    MethodInt int // 方法的 int 和   9 代表 GET 和 DELETE 方法}

上述表結構 PathsDataVerify key="1_/template" value=true 因為 1 表示 GET 所以這種表示描述就為 模版的查詢方法,需要開啟資料認證許可權
roleName+Typ 作聯合唯一索引

有了 Path 與角色的關聯,就自然會有 Path 的詳情,以方便管理,作為組成角色的許可權的源,同時可以設定該介面是否開啟資料驗證許可權。

type RouterDoc struct {    Path      string                   // 請求路徑  /template    Desc      string                   // 路徑實現功能的大類, 例如 模組的 CRUD    MethodMap map[string]MethoedDetail // key 方法 具體表現  "1"  "2" ..}type MethoedDetail struct {    DataVerify bool   // 該方法是否開啟 資料驗證    Desc       string // 模版的刪除 則 MethodMap key = 8}

通過上面的簡單表述,就可以建立 url + method 等於方法的實現,可以很方便的進行許可權管理
Path 作唯一索引

那麼我們的資料許可權驗證又是怎麼實現的呢。它與角色許可權又是什麼樣的關係。與人又怎麼綁定的,註解如下

type SignDoc struct {    SignKey      string         // 簽名, 某人的私人簽名 + 使用者唯一標識  可以理解成 這個人擁有這個簽名的哪些資料許可權    CreateUserId string         // 簽名的建立者    UserId       string         // 這個成員也擁有這個簽名的一部分/全部許可權, 相當於建立者把自己的簽名共用出,用於多人使用    RouterMap    map[string]int // key=Path value=Method   $bitsAllSet 計算  {"/template":14} 表述 這個人擁有此簽名下面的模版資料 增/改/刪的許可權}

SignKey+UserId 作聯合唯一索引

這樣建立者可分配的許可權為自己的角色許可權的子集,可以把一個私人key 暴露成公用的授權個他人,那麼他人就對此簽名下面所有的資源,都有相應你配置的許可權,RouterMap 代表他擁有哪些許可權,這樣你就可以把自己的簽名,分給不同的人,不同的人,擁有對此簽名不同的許可權。完全實現了,自己的資料自己做主管理。用於多種協調工作的情境。比如臨時有一個修改活動邏輯的任務,有3個人做,那麼他們組負責人可以建立一個公用的 key ,賦予這三個人,用於此次任務的所有許可權操作。

所有的表結構都註解了,那麼它們是怎麼關聯協作的,下面我們將從一個使用者的角度使用許可權系統裡面所涉及的 sql 語句做簡單解析,以便熟悉整個請求的流轉。

配置許可權流程

初始化路由 Method 描述以及是否開啟資料許可權 ---> 建立角色 ---> 把才建立好的路由與建立的角色進行綁定 ---> 為角色添加使用者 ---> 角色建立自己的簽名 ---> 每一次請求攜帶上籤名 ---> 建立資源的時候攜帶上此簽名與資源綁定

查看個人自己擁有什麼許可權

    // userid 查詢 RoleDoc 然後把所有返回的 PathMethods 做彙總    // 通過 PathMethods 也可以結合 RouterDoc 查詢具體的資料驗證許可權

查看別人授與權限

    // userid 查詢 SignDoc  然後對返回,做彙總即可

在許可權的配置和管理上面,沒有任何複雜的 sql ,當然在實際項目當中有很多的 sql 組合來滿足不同的配置方式。總體下來是很方便的。

複雜分配舉例,以對資源的 CRUD 為例

  1. u1 u2 u3, u1 擁有 u2 簽名 u2-s-1 下的 ceph資源讀取許可權, u2 擁有 u1-s-3 下的 template CRU 許可權,u3-s-1 下的ceph CU 許可權。 u3擁有 u1-s-3 所有資料的 R 許可權

資料儲存表現

// RoleDoc[    {"userId":"u1", "signKey":{"u1-s-3":"ceph kv manager"}},    {"userId":"u2", "signKey":{"u2-s-1":"template manager"}},    {"userId":"u3", "signKey":{"u3-s-1":"template manager"}}]
// RouterDoc[    {"path":"/ceph","methodMap":{        "1":{"dataVerify":true,"desc":"query ceph value"},        "2":{"dataVerify":true,"desc":"create ceph value"},        "4":{"dataVerify":true,"desc":"update ceph value"},        "8":{"dataVerify":true,"desc":"delete ceph value"},        }    },    {"path":"/template","methodMap":{        "1":{"dataVerify":true,"desc":"query template value"},        "2":{"dataVerify":true,"desc":"create template value"},        "4":{"dataVerify":true,"desc":"update template value"},        "8":{"dataVerify":true,"desc":"delete template value"},        }    }]
    // RoleDoc 此角色沒有刪除許可權[    {"reoleName":"ceph&template manager","userIds":["u1","u2","u3"],    "pathMethods":[        {"path":"/ceph","methodInt":7},        {"path":"/template","methodInt":7}    ]}]
    // SignDoc 簽名資料許可權關聯[    {"signKey":"u1-s-3","createUserId":"u1","userId":"u2","routerMap":{"/template":7}},    {"signKey":"u3-s-1","createUserId":"u3","userId":"u2","routerMap":{"/ceph":6}},    {"signKey":"u2-s-1","createUserId":"u2","userId":"u1","routerMap":{"/ceph":1}},    {"signKey":"u1-s-3","createUserId":"u1","userId":"u3","routerMap":{"/template":1,"/ceph":1}}]

當資料量特大的嵌套文檔,都可以外鍵單獨行儲存。

驗證許可權

使用者(統一單點)登陸後 ---> 判斷是否為初始使用者(預設角色) ---> 返回角色類型,擁有哪些許可權(前端可以根據擁有的許可權對 ui 進行渲染控制)

使用者請求 攜帶 userid + signkey 請求擷取 path + method (路由如果支援 動態路由, 用 httprouter 解析成後台配置的路由)
使用 userid + path + method 查詢 roleDoc 表 驗證是否存在角色許可權(如果角色為超級管理員,則後面無需驗證)
根據角色許可權 PathsDataVerify 欄位 value 值可以判斷是否驗證資料許可權。
如果需要判斷資料許可權,則 攜帶 userid + signkey + path + method 查詢 signDoc 表,是否存在 有許可權則把相應的 許可權資訊寫入 context 供後續方法使用

如果是查詢介面 使用者無需攜帶 signkey , 需要查詢的 signkey 由許可權處理中間方法計算群組成 signkey 數組 , mongodb 使用 $in 查詢資料
signkey 數組: 自己的私人 key , 別人授權的資料許可權 key 通過 userid + path + method 查詢到所有授權的 signDoc 把裡面的 signkey 放到數組裡

總結

這一套許可權驗證是解決之前固有依賴部門設計的許可權而提出的,此許可權不存在與其他的依賴,只取決與簽名與人與路由之間的綁定關係。可以任意組合!
資料許可權是角色許可權的子集,必須要滿足角色許可權。比如 u1 沒有刪除 /template 的角色許可權, u2 授權 u1 擁有 u2-s-1 的 /template 的刪除許可權,也是不可以的。
通過這套許可權把之前的資料進行了拆分,結構便於理解。當然此套表結構也是適用於其他資料庫的。如果對效能有要求是可以用 redis 對許可權做緩衝的。當然目前我們基於200 + 使用者, 200+ 路由配置,許可權過濾和所有的管理結構都是在 20ms 下的。對於後台系統是可以接受的。

設計不好,僅供參考

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.