作者:_liuxx

cnblogs.com/liuyh/p/977

1、數據許可權概述

1.1、什麼是數據許可權?

數據許可權是指對系統用戶進行數據資源可見性的控制,通俗的解釋就是:符合某條件的用戶只能看到該條件下對應的數據資源。那麼最簡單的數據許可權大概就是:用戶只能看到自己的數據。而在正式的系統環境中,會有很多更為複雜的數據許可權需求場景,如:

  • 領導需要看到所有下屬員工的客戶數據,員工只能看自己的客戶數據;
  • 經理A能看到所有企業客戶,經理B只能看到年銷售額小於1000萬的企業客戶;
  • 角色A能看到全國的產品數據,角色B只能看到上海的產品數據;

上述這些需求,使用硬編碼也是可以實現的,但是在業務快速發展的過程中,類似這種數據許可權需求會越來越多,如果全部採用硬編碼的方式,無疑會給我們帶來巨大的開發和維護壓力。

1.2、要素分析

從當前登錄用戶的角度來說,數據許可權的定義可以解釋為:當前登錄的用戶只能看到該用戶許可權範圍內的數據資源。由此可以分析出數據許可權控制中幾個關鍵要素:

  1. 主體,即當前登錄用戶。領導、角色等概念可翻譯為當前登錄用戶是否是領導,是否擁有某角色。
  2. 數據資源。即受管控系統數據。
  3. 條件規則。即當前登錄用戶對於某特定的數據資源適用的條件。

2、數據許可權設計

理論上來說,用戶在訪問受控的系統數據時,獲取用戶對該數據資源適用的條件規則,並將該條件規則解析為SQL查詢語句即可實現對數據的許可權控制。但是在實現過程中,還是會有很多難點,譬如當前登錄用戶適用下列規則:

客戶數據:[客戶經理] [包含於] [下屬人員]

產品數據:[銷售地區] [等於] [上海]

訂單數據:([產品銷售地區] [等於] [上海])[並且] ([客戶市場經理] [包含於] [下屬人員])

思考如下問題:

  1. [客戶經理] [包含於] [下屬人員]如何解析為SQL語句?多表聯合查詢時又該如何處理?
  2. [下屬人員]由系統根據當前登錄用戶計算而來,上海由管理員後臺選擇。兩種方式如何兼容?
  3. 對於複雜多變的組合條件,應該如何設計?
  4. 如何確定當前查詢應該應用哪些條件規則?
  5. 一個用戶擁有多個角色,不同角色對於同一個規則設置不同的值應該如何處理?

2.1、規則元

名詞定義:規則元。在本文是指單個獨立的數據規則定義,不同用戶對規則元可設置具體的規則過濾值,該值用作數據查詢時的篩選條件。上述規則中[客戶經理][銷售地區]都屬於規則元。

2.2、規則元配置

1.規則元名稱的配置。一個表中哪些欄位可以進行規則設置,以及規則元名稱如何與表欄位關聯。(如上述規則中[客戶經理][銷售地區]),比較容易想到的方法是通過配置文件維護規則名稱與資料庫欄位之間的關係。

2.規則元Value數據源的配置。如上述規則中的[下屬人員][上海],不難發現規則元Value來源有三種情況:

  • 後臺管理人員輸入。
  • 系統提供數據源,後臺管理人員選擇。如:所在地區[上海]
  • 系統提供數據。如:[下屬人員]

配置文件可以實現數據規則的配置需求,但是當規則元越來越多時,維護配置文件就會變得麻煩起來,我們是否可以效仿spring-boot取代spring-mvc的做法,使用註解來代替配置呢?每一條數據規則最終都會落到對資料庫欄位的控制,而現在絕大部分系統都會有一個Model層對應到資料庫中的表,於是腦補出一個絕佳的規則元配置方式:

@TableName("test")
public class TestModal extends AbstractModel {
@DataRule(name = "規則元名稱")
private String name;
}

@DataRule註解源碼如下:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataRule {
  /**
  * 規則元名稱
  */
  String name() default "";

  /**
  * 規則元值來源類型
  */
  RuleSourceStrategy strategy() default RuleSourceStrategy.TEXT;

  /**
  * 當數據來源是用戶選擇時{@code RuleSourceStrategy.CHOICE}數據地址
  */
  String url() default "";

  /**
  * 當數據來源是系統提供時{@code RuleSourceStrategy.SYSTEM}提供器類名
  */
  Class<? extends IDataRuleProvider> provider() default NullDataRuleProvider.class;
}

系統啟動時,將規則元配置信息(名稱、對應數據表、對應欄位、值來源類型,值來源url,值來源提供者類名等)同步至資料庫。數據表簡單設計如下圖:

2.3、數據規則的配置

有了規則元信息,管理人員即可在系統中針對不同用戶(角色)設置規則元Value,該值作為數據查詢時的篩選條件。規則元Value數據源包含三種情況,其中第①、②種情況下,需要管理員填寫或選擇該規則的值,存儲於資料庫;第③種情況下,Value值根據當前登錄用戶計算得出,也即是@DataRule註解中provider計算得來的值。由資料庫存儲的規則與系統計算得到的規則合併後即是登錄用戶的所有數據規則。

一個簡單的配置界面如下:

2.4 數據規則的解析

由上文可知,適用於當前登錄用戶的數據規則主要來源有兩種:

  1. 存儲在資料庫中的規則配置;如:所在地區[上海]
  2. 需要系統計算的規則配置;如:[下屬人員]

兩種情況下獲取的數據規則合併之後即可獲取適用於當前登錄用戶的數據規則集合,流程圖如下:

兩種情況下獲取的數據規則如何兼容?規則合併後成為一個複雜的查詢條件應該如何設計?

定義通用的規則結構如下:

{
rule:[{
field: "name",
operate: "equal",
value: "xxx"
}],
operate:"and",
group:[{
rule:[],
operate:"greater",
group:[]
}]
}

資料庫存儲規則結構的JSON串,合併時將JSON串反序列化之後使用and與系統計算得出規則對象連接即可,合併後的規則結構解析成簡單SQL語句已經不是很難了。

但是對於多表聯合查詢時應該如何處理呢?

解析成SQL語句時可以使用表名+欄位名的方式,可是遇到查詢中使用別名的時候,這種方式也不能正常工作,這裡暫時的處理方式是支持解析時傳遞別名。

一個用戶擁有多個角色,不同角色對於同一個規則設置不同的值應該如何處理?

譬如,用戶A擁有角色role1role2,其中:

role1適用規則:[銷售地區] [等於] [上海]

role2適用規則:[銷售地區] [等於] [北京]

那麼用戶A合併後的數據規則應該是:

用戶A適用規則:([銷售地區] [等於] [上海]) or ([銷售地區] [等於] [北京])

即:一個用戶對於同一個規則元的多個規則設置,應使用or連接後再與其他規則元進行and連接。

2.5、確定當前查詢適用的數據規則

經過上述的規則配置與解析之後,我們很容易拿到當前用戶適用的數據規則集合。但是在一次查詢時我們應該使用集合中哪些規則進行過濾呢?一次查詢是否開啟數據規則過濾,使用哪些表的規則過濾應該是開發者來決定,類似:

xxxQuery(...).withDataRule("`table1`,`table2`");

即表示當前用戶本次查詢使用table1table2中配置的數據規則。數據表中的每條規則應該支持在管理後臺設置是否啟用,這樣理論上可實現每個用戶對每一條數據規則的配置。


推薦閱讀:
相關文章