前言:“我們有一個訂單清單,希望能夠根據目前登陸的不同使用者看到不同類型的訂單資料”、“我們希望不同的使用者能看到不同時間段的掃描報表資料”、“我們系統需要不同使用者檢視不同的生産報表列”。諸如此類,最近經常收到項目上面的客戶提出的這種問題,即所謂的“資料權限”,經過開會讨論決定:在目前的開發架構上面搭建一套通用的資料權限功能。
本文原創位址:http://www.cnblogs.com/landeanfen/p/7760803.html
有了上面的引言,自然而然就引出了今天需要和大家讨論的話題——資料權限。作為開發人員,我們肯定知道,一般的系統都離不開權限子產品,它是支撐整個系統運作的基礎子產品。而根據項目類型和需求的不同,權限子產品的設計更是大相徑庭。但不管怎麼變,權限子產品從大的方面來說,可以分為兩種大的類型:功能權限 和 資料權限。
功能權限:主要控制不同的資源主體(使用者、角色、組織等)有操作不同的資源的權限。比如常見的不同的角色能通路不同的頁面(菜單權限),以及具有操作同一頁面的不同功能(按鈕權限)等等,都資料功能權限的範疇,這種設計相對比較簡單,也比較為大多數系統所通用。當然網上資料、設計思路也可以找到很多。
資料權限:主要控制不同的資源主體(使用者、角色、組織等)有檢視不同的資料資訊的權限,一般來說,資料權限又分為資料行權限和資料列權限,通過字面意思不難了解這兩者的差別,比如上文“我們有一個訂單清單,希望能夠根據目前登陸的不同使用者看到不同類型的訂單資料” 這就是一個典型的資料行權限,而“我們系統需要不同使用者檢視不同的生産報表列”這就是資料列權限的範疇。由于資料權限和系統的業務邏輯關系非常密切,是以不同的系統設計差異性會非常大。從另一方面來說,由于資料權限和業務邏輯關聯性非常強,如果系統的業務邏輯非常複雜,資料權限設計起來也會相對複雜,是以關于資料權限的設計一直沒有一種相對通用和使用簡單的設計方案。
如果你動手去設計資料權限,當你去各大平台、百度、谷歌查找設計思路的時候,你會發現很難找到有用的資料,很多設計思路局限性非常大。其實原因很簡單:資料權限的設計和他人系統關系緊密,一般不太容易拿到你的項目上面直接使用。
當然也有另外部分人說“資料權限并不能作為權限子產品去設計”,比如部落客看到這樣一條評論

從一定程度上來說,這樣了解也不為過,如果你覺得你的系統靈活性和配置性不需要那麼高,把資料權限的規則在代碼裡面寫死又何妨,部落客所在公司的另外一個部門就是這麼幹的,除了編碼量大一點,其實也沒什麼太大的問題!其實部落客說這麼多無非是想表達一個觀點:沒有絕對通用的資料權限設計思路,關鍵看合不合你用!當然本文的設計思路也是一樣,不強制要求,不提通用。設計思路供你參考!
關于權限設計的雜談就告一段落,凡是點到即止,再說了多了就說爛了。到目前為止,部落客找到的一篇寫得相對比較好的文章 通用權限管理設計 之 資料權限。這位部落客是用sql去實作的,如果是把這個運用到EF裡面的話,考慮到EF複雜的導航屬性,會有一些問題。接下來說說部落客這邊想到的設計思路。
先說說部落客所在項目的情況,和資料打交道的部分采用EntityFramework+Repository的傳統模式去實作的,整個項目從上到下,就是一種典型的"僞DDD",什麼是”僞DDD“?這裡不做過多說明,使用過DDD的同仁應該很清楚。下面是設計思路流程圖:
第一步:配置資料規則
第二步:頁面使用資料規則
以上是一個大緻的思路圖,總的來說,要實作基于EF的資料權限設計,主要分為兩大步驟
配置資料規則這裡有三個大的方面:功能子產品、資料資源、角色
功能子產品:為什麼這裡要加上功能子產品的限制?是因為部落客覺得我們某一個頁面在查詢資料的時候,會有一個查詢的範圍,比如訂單查詢頁面肯定隻能查詢和訂單有關聯的實體功能,而不可能查詢和它沒有任何關聯的業務。加這個限制更大的意義在于我們動态的構造Lambda去查詢實體的時候不會産生”找不到相關聯的實體“之類的錯誤。
資料資源:具體對哪種資料資源做資料權限,比如訂單的狀态不等于取消狀态、訂單的下單時間小于目前月份等等。
角色:資料資源的主體,還可以是使用者、部門、組織等等。
這三者配置之後得到的一個結果就是某一類角色的某一個功能子產品對哪個資料資源的資料規則是什麼樣的。比如有一條銷售總監的資料規則,配置銷售總監在訂單子產品裡面訂單這個實體的訂單類型是銷售訂單的所有資料,這就是針對銷售總監在訂單子產品的資料規則。可能最終資料庫存儲得到的資料類似這樣:
RoleId
FunctionCode
Rules
2
OrderQuery
{"rules":[{"field":"Order_Status","operate":"in","value":"[0,1,2]"},{"field":"Order_Type","op":"equal","value":"1"}],"logicoperate":"and"}
3
{"rules":[{"field":"Order_Status","operate":"in","value":"[0]"},{"field":"Product.Categary.Type","equal":"equal","value":"1"}],"logicoperate":"and"}
5
Product
{"groups":[
{"rules":[{"field":"Order_Status","operate":"in","value":"[0,5,10]"},{"field":"Order_Type","op":"equal","value":"1 "}],"logicoperate":"and"},
{"rules":[{"field":"LineName","operate":"equal","value":"fenzhuangxian"}]}
],"logicoperate":"or"}
需要特别說明的是:由于EF有導航屬性,這裡的Rules在儲存的時候如果遇到導航屬性,我們的字段值需要這樣儲存——Product.Categary.Type。因為在我們轉換成為lambda表達式的時候導航屬性會是這樣寫:x=>x.Product.Categary.Type==1。這個我們在後面使用這個規則的時候加以說明。
有了上面的資料規則,接下來就是我們在取資料的時候如何使用了,這裡有一點需要說明的是:我們這裡需要傳兩個參數,一個是子產品的名稱,比如上面的OrderQuery、Product等;第二個是目前使用者的角色id,這個可以通過目前登陸使用者的id擷取到角色。
要使用資料規則,之前部落客分享過兩篇關于動态Lambda的文章,現在派上用場了。隻不過原來隻是一些基礎類型轉lambda,現在涉及到了導航屬性,不知道是否可行。部落客查閱了一些資料,最終找到了解決方案。
然後測試如下
測試得到的查詢lambda結果為x=>x.Product.Categary.Type=="1",測試成功!
對于配置資料規則的時候還有一點比較麻煩的是,如果如何知道哪個功能子產品使用哪些實體?不可能直接讓使用者去寫Product.Categary.Type這些複雜的功能吧,如果是這樣,談何體驗。那麼隻有使用另外一種解決思路了——反射EF實體。
反射EF實體的時候如果是導航屬性,還得繼續反射導航屬性的實體,這樣一層一層反射下去,最終确實是可以得到形如Product.Categary.Type這個的結構體,但界面如何展現還有待思考。比如思路如下:
以上隻是一個設計思路,理論上來說是可以實作的,如有不足,歡迎斧正,謝謝。如果思路沒有問題,後續部落客會抽時間将這種設計的實作過程展現出來供大家參考,歡迎關注。其中的難點有兩個:
1、逐級反射EF的導航屬性,以及這個過程如何展現。是通過特性标記,還是開發人員配置;
2、動态Expression在構造Lambda的時候和配置資料的相容性問題,比如資料類型的相容性有點難控制。
本文原創出處:http://www.cnblogs.com/landeanfen/
歡迎各位轉載,但是未經作者本人同意,轉載文章之後必須在文章頁面明顯位置給出作者和原文連接配接,否則保留追究法律責任的權利