天天看點

基于二進制控制系統權限

二進制是什麼?

在數學和數字電路中指以2為基數的記數系統,以2為基數代表系統是二進位制的。這一系統中,通常用兩個不同的數字0和1來表示。在計算機中,最常用的是二進制,因為組成計算機系統的邏輯電路通常隻有開和關這兩個狀态,用0和1很好表示這兩種狀态。

二進制的使用

二進制可以控制系統的權限,說的簡單點,即通過二進制0/1的位置來控制權限,因為某個功能要麼有權限,要麼沒有權限

例如有一個場景:使用者登入某個用戶端(平闆、手機、網頁),要通過用戶端的來源判定他是否有通路權限,我們可以通過二進制的權限位置,從最後一位往前(0位、1位、2位),設定3種權限:

可以通路平闆:00000001

可以通路手機:00000010

可以通路網頁:00000100

假設我們配置使用者user1,可以通過平闆和網頁通路,即用二進制表示為00000101,用十進制表示為:5

當使用者使用手機登入時,擷取應用的權限00000010(一般每個應用調用登入接口都會攜帶一個appKey,這個appKey綁定了它所屬的權限,根據appKey可查到對應的權限),十進制為:2,那怎麼判定使用者是否可以登入到目前應用呢?

通過&運算,功能權限 & 使用者擁有的權限 == 功能權限?

即:2 & 5,轉換為二進制:

00000010
& 00000101
-------------
  00000000           

&運算,隻有兩個位都是1,對應位才是1,

隻有當2&5 == 2時,表示目前使用者有手機登入權限。

二進制存儲的優點

我們設計一個權限系統,一般都會基于RBAC模型,即使用者-角色-權限,表設計如下:

基于二進制控制系統權限

正常做法,使用者通過一個角色關聯多個權限,資料存儲如下:

基于二進制控制系統權限

這僅僅是一個角色,如果一個系統需要關聯的角色很多,那麼這張表将會存儲大量的資料

通過使用二進存儲可以大大減少資料量的存儲,我們知道JAVA中Long類型可以轉換為64位二進制,

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000           

如果把每一個位置來表示一個權限,除去最左邊的符号位以外,可以表示63種權限,即一個角色最多可以關聯63種權限,如果一個角色關聯了63種權限,那麼我們隻需要插入一條資料即可,但在複雜的系統中,63種權限肯定是不夠的,可以通過設計一個權限空間來進一步區分,比如最初權限空間為0可以表示63種,當超過63種權限,權限空間遞增,然後再可以表示63種權限,是以我們對之前的角色、權限表進行改造:

基于二進制控制系統權限

角色-權限新增/删除

當使用者在界面上新增,或删除權限,我們又該如何更新二進制權限的資料?

增權重限

比如有一個角色,對應的權限空間為0,權限二進制是00001111,需要添加一個,權限空間為0,二進制為:00100000,二進制位置為5,使用二進制的“|”運算符,規則如下:

原二進制權限 | 要添加的二進制權限           

通過二進制位得到要添加的二進制權限

1 << 5 = 00100000

再和00001111 進行或運算

00100000
| 00001111
-----------------
  00101111           

00101111即為最新的權限

删除

還是以上面新增為例,00101111需要删除添加的權限00100000。

原則:

原二進制權限 & (~要删除的二進制權限)

也就是:

00101111 & (~00100000)

~00100000 表示取反,得到11011111,然後和原二進制做&操作

11011111
& 00001111
-----------------
  00001111           

判斷使用者是否具有權限

判斷使用者權限同上面使用者通過用戶端通路的例子,原則:

功能權限 & 使用者擁有的功能權限 == 功能權限           

通過角色查詢權限

資料庫表都存儲的是二進制資料,我們如何通過一個角色查詢到它所有的權限呢?

先上SQL:

SELECT
*
FROM
 角色表 r
LEFT JOIN 角色-權限關聯表 rp ON r.角色ID = rp.角色ID
LEFT JOIN 權限表 p ON p.權限空間 = rp.權限空間
WHERE
CONV(rp.權限二進制,2,10) >> p.權限二進制位 & 1 = 1
and r.角色名稱 = ?;           

重點介紹CONV函數:

CONV(N,from_base,to_base) 表示轉換進制, N是列名或值, from_base是從什麼進制,to_base是轉到什麼進制
CONV(rp.權限二進制,2,10) 就是從二進制的01串變成十進制的數           

比如:權限二進制為00001111,通過CONV轉換為十進制得到16

有一條權限資料為:00001000,他的二進制權限位置為3,将 16 >> 3 得到 00000001,即将所在的權限位置移動到了最後一位,隻要和1做 & 操作,得到的結果如果是1,那麼表示包含了這條權限資料

根據權限查角色

SELECT
*
FROM
權限表 p
LEFT JOIN 角色-權限關聯表 rp ON p.權限空間 = rp.權限空間
LEFT JOIN 角色表 r ON r.角色Id = rp.角色Id
WHERE
CONV(rp.權限二進制,2,10) >> p.權限二進制位 & 1 = 1
and p.權限名稱 = ?           

通過二進制存儲資料的好處,大大降低了資料量,原來63條資料可以隻用一條資料就可以代替,進行權限判斷時,原來需要從幾十甚至上百的資料一條一條判斷,現在僅僅需要進行位運算就可以了,位運算一般比前面的邏輯高效的多。

但也有缺點,資料庫存儲的都是二進制,可讀性降低了,如果項目交接給其它同僚,還得對于權限空間和權限位置這些字段含義詳細介紹,并且查詢使用到了各種函數轉換,這些字段的索引也失效了

總結:一般對于使用者權限功能個數确定,比如一個供應商有幾十萬門店,使用者、角色、權限數量不多,可以考慮使用二進制來控制權限提升效率、減少存儲資料量。

本文來源于公衆号《百川分享會》:baichuanshare
基于二進制控制系統權限