通用數據權限的思考與設計

1 數據權限概述spring

1.1 什麼是數據權限?數據庫

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

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

上述這些需求,使用硬編碼也是能夠實現的,可是在業務快速發展的過程當中,相似這種數據權限需求會愈來愈多,若是所有采用硬編碼的方式,無疑會給咱們帶來巨大的開發和維護壓力。

1.2 要素分析mvc

從當前登陸用戶的角度來講,數據權限的定義能夠解釋爲:`當前登陸的用戶只能看到該用戶權限範圍內的數據資源`。由此能夠分析出數據權限控制中幾個關鍵要素:ide

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

2 數據權限設計編碼

理論上來講,用戶在訪問受控的系統數據時,獲取用戶對該數據資源適用的條件規則,並將該條件規則解析爲SQL查詢語句便可實現對數據的權限控制。可是在實現過程當中,仍是會有不少難點,譬如當前登陸用戶適用下列規則:url

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

 

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


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擁有角色`role1`、`role2`,其中:

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

 

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

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

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

 

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

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

 

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

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

相關文章
相關標籤/搜索