天天看點

web權限管理設計(1)——設計的要點分析(2)

接着上文,分析到了繼承給系統帶來的困難。

就目前而言,我想不到能夠完美解決問題的方案,但是我認為還是不能抛棄繼承。因為他确實能給管理者減少很大的工作量。

程式的一大好處就是代替使用者做重複性工作,顯然這裡給管理者減少工作量就是一個很好的方向,哪怕這個程式執行的慢一點,那也比人工好很多。

再回到上文說的,權限管理系統該有什麼的特點:

  1. 管理者能夠清晰的對權限進行管理,也就是那個管理界面,是符合正常人的思維的。
  2. 權限校驗的性能要高,不能因為這個對整個系統的性能産生太大的影響

管理界面

假設我們是給一個擁有10萬員工的超大型公司設計權限管理系統,那麼我估摸着大概會有1000個角色。

那麼如果不使用繼承,那麼我們的角色管理界面,就是一個表格[角色id,角色名,資源清單],那麼這個清單就會有1000條資料,并且僅能通過名字判斷它大概是給誰用的,因為資源清單就是幾十個上百個資源id,用逗号分隔,根本無法通過資源清單去判斷。

而使用了繼承,可能會導緻角色數量增加,因為為了更好的繼承,角色的設計往往需要更細緻。

但是有了繼承資料,就可以生成一個連結清單圖或者别的,讓他們的關系更明顯。當然這個難度有點大。

退一步講,即使為了簡單,界面依然是個普通清單:[角色id,角色名,繼承角色,資源清單],這個清單也還是和沒有繼承時那麼難看,難用。但是,繼承并沒有給管理界面帶來什麼新的問題,還帶來可一個優化的方向,盡管這個方向比較難實作。

确實,就目前的想法而言,繼承不能讓界面更合理更好看,但是也沒有讓界面變得更難看。

性能

一個系統對性能的考慮,一般最優先考慮使用者。因為他們才是使用主體,而對于管理者,慢點好像也還能接受。畢竟他們的數量和使用者相比,太少了。

查詢

那麼權限管理系統與使用者最相關的就是每次通路時的鑒權。也就是權限的查詢。

上一篇文章裡分析了,ACL最直接,查詢性能極高。但是這不現實。RBAC的出現會讓權限的查詢繞彎彎。但是這才符合現實。

前面我們設計了對使用者的授權隻能使用角色組,而角色組不能直接擁有資源,得通過角色去擁有,以至于查詢權限多了很多個環節。

對于一個千萬使用者的系統,通過角色和角色組的封裝,可能最終的角色組的數量僅有1000個。如果我把這些角色組加載到redis裡去(1000個key也不算多嘛),而不是放在資料庫中,那麼,查詢的時候是不是就快了很多了?

但是前面的設計思路中,角色組不能直接擁有資源,我們查詢到使用者所在的角色組之後,依然要去查詢對應的角色的權限,難道我再去資料庫裡查?那把角色組放到redis的意義就不大了。

要麼,把角色也加載到redis中,那麼redis中key的數量就太多了。不理想。是以,上述方案按照原來的設計思路行不通。

為了加快查詢的效率,我考慮讓角色組直接擁有資源。那麼,前面的分析的東西不就白費了?其實不是的。前面分析的理論,是設計系統的基礎,權限系統的設計依然按照上述思路去設計。我僅僅是讓角色組增加一個字段:資源。對,就是角色組通過角色擷取到的所有資源。

這個資源字段的出現,僅僅是為了加快查詢的效率。這樣查詢一個使用者是否具有通路某個資源的權限,隻需要:

  1. 查詢目前通路的資源的id
  2. 去資料庫查詢其所屬的角色組,可以有多個
  3. 然後去查詢這些角色組的資源字段裡是否包含這個資源,隻要有一個角色組包含,該使用者就可以通路該資源。

最後一步因為是在redis中查詢,是以性能會高很多。而前兩步,不管怎麼樣都需要(當然用别的方案優化了也可以都不需要)。

角色組的這個資源字段是系統根據所擁有的角色生成,連系統管理者都無感覺,也不能直接修改它。想修改它,隻能通過修改角色組的角色,是以,前面講的所有理論依然存在且被使用。

PS:在實際的系統設計中,還有更細節的優化,這裡就不展開,後面再講。

增删改

這三個操作僅有管理者可以使用,是以,這部分的性能,慢點就慢點吧。讓管理者忍忍,盡量保證一個操作能在三秒内完成,應該在可以接受的範圍内。

“增”對已有的角色和角色組不造成直接影響,是以沒什麼問題,這裡不再分析。主要是删和改。

為了優化查詢性能,新增了使用者組的資源字段,就因為這個字段的存在,讓删和改變得複雜了許多。

舉個例子,角色組A,包含了角色(1,2,3,4),而角色3又繼承自角色5。那麼我修改或者删除角色5,就會影響到角色組A,是以就得更新角色組A的資源字段。

更要命的是,我并不知道角色5會影響到角色組A,因為它并不直接擁有角色5。那麼該如何捕獲這種變化呢?

說實話,想不到什麼好方法,因為根本無法确定哪些上級角色和角色組會受到角色5的影響。下面再細緻點分析這些變化。

分三種:

  1. 删角色組,因為規定隻要角色組及其下級隻要還存在成員,就不允許删除,是以能被删除的角色組,就不會影響其他角色組和角色。
  2. 删角色,首先,會影響到直接繼承自該角色的角色,這個好辦,直接搜尋誰的繼承角色id是被删除的角色id,就可以确定了。 其次,會影響到直接或間接包含了該角色的角色組。
  3. 直接删除某個資源,首先查詢角色裡誰包含這個資源,可以直接删除;其次,角色組也需要查詢資源字段裡是否有該資源,也可以直接删除。

分四種:

  1. 修改角色組包含的角色,這個比較好辦,直接用修改後的角色計算資源字段即可
  2. 修改角色包含的資源,這會影響角色組,不會影響其他角色。
  3. 修改角色的繼承關系,分為取消繼承和修改繼承。對角色不會有影響,但是對某些角色組會影響。
  4. 修改資源的名字啥的,不影響,因為是使用id關聯。id是系統生成,不允許手動修改
改進方案

上面的麻煩均由于角色組增加了資源這個字段引起的。本身不管增删改都不需要操心。因為都是動态查詢的。

但是現在為了更新這個資源字段,不得不捕捉這些更新。

是以首要判斷哪些角色組會受到影響,最開始的方案是:每一個角色組的每個角色,都層層周遊下去,查詢是否與這個變動的角色相關,如果相關,那麼,就要重新計算這個資源字段。對,沒錯,幾乎就是要全局周遊,最要命的是角色層層繼承,整個搜尋深度很深。是以性能很差。

我想到的一個小小的優化是:實際上任何會影響到角色組的資源字段的變化,本質上都是某些資源的變更。變更的方向就兩個,增加資源和減少資源:

  1. 對于增加資源,如果這個增加的資源,某角色組本身就已經有了,那麼這個資源的增加對該角色組無影響。
  2. 對于減少的資源,如果這個角色組本身就沒有該資源,那麼這個資源的減少對該角色組無影響。

是以,角色組資源字段更新的幾個步驟:

  1. 分析一次變更的資源,增加了哪些,減少了哪些
  2. 所有角色組的資源字段,都按照上面的分析思路進行對比,這些資源的增加和減少是否有可能影響。隻要判斷出哪個資源可能影響到了該角色組,那麼直接停止分析,直接對該角色組的資源字段進行完全更新。
  3. 如果一個角色組的經受了上面所有增減資源的考驗,那就說明它不需要變更。

比如說,我删除了角色5本身擁有的資源001,這個修改,不會對其他角色造成影響,是以不需要去周遊角色。但是角色組就需要周遊了,周遊的目的就是确認資源裡是否擁有資源001,對于擁有資源001的角色組,不猶豫,直接重新計算一遍資源。對于沒有資源001的角色組,不需要更新,因為如果它直接或間接繼承了角色5,那麼它一定擁有資源001。在詳細一點縷清這個關系:

  1. 某角色組沒有資源001,是該角色組與角色5沒有關系的充分必要條件
  2. 然而, 某角色組擁有資源001,是該角色組與角色5有關系的必要不充分條件

這樣做的好處是避免角色組進行全局深度搜尋。

當然,上面思路我還沒實踐,僅僅是理論分析能優化性能。退一萬步講,哪怕我每一次更新都對角色組做全局更新,對于大多數系統的那點資料量,也不是什麼大問題。你說你系統使用者量上百萬,那麼,請花錢請專業團隊設計。

在之後的真正編寫代碼的環節中,肯定也會想到更多的優化方案。我有信心将所有的接口響應時間控制在3秒内。