1、二進制做權限的優點
大家都知道,在Linux操作系統中,x - 可執行權限,w - 可寫權限 , r - 可讀權限。其權限值分別是1,2,4,但是有沒有想過為什么是1,2,4 而不是 1,2,3 呢?
OK , 現在是不是發現 1,2,4 分別對應着2的冪次方(2^0、2^1 、2^2),在計算機中都是以二進制的方式進行存儲,在計算時二進制的方式會更快。舉個例子:如果一個人擁有讀和寫的權限,現在他的權限值為6,當需要判斷他是否擁有寫權限時,只需要用 6 和 2 進行按位與運算(6 & 2 = 2),結果非0 ,所以可以判斷擁有此權限。當需要判斷他是否擁有可執行權限時,同樣只需要用 6 和 1 進行按位與運算 (6 & 1 = 0 ),結果為0,所以可以判斷不擁有此權限。
6 & 2 = 2 00000110 & 00000010 ——————---- 00000010 又或者:6 & 1 = 0 00000110 & 00000001 ——————----- 00000000
為什么只需要用用戶的權限值和對應的權限進行按位與運算就可以判斷出是否擁有此權限呢??個人理解為:當每個2的冪次方分別代表一個權限時,剛好能夠和對應的二進制位對應起來。當用戶擁有此權限時,對應的權限值的二進制位會變為1,然后進行按位與運算,從而可以知道是否擁有此權限。
用二進制的方式除了可以加快速度,還有沒有其他優點呢?如果在一個應用系統中,我們應該如何用二進制的方式來進行權限管理呢?
想要知道二進制的方式的優勢就需要和一般方法進行比較,OK。
不采用二進制時數據表的設計: user - 用戶表 id name 1 AA 2 BB 3 CC permission表 id name method/url 1 查看帖子 get 2 發布帖子 post 3 修改帖子 update 4 刪除帖子 delete 用戶-權限對應表 id uid perId 1 1 1 2 1 3
采用二進制權限方法時數據表的設計: user - 用戶表 id name per_value 1 AA 6 2 BB 7 3 CC 4 permission表 id name method/url value 1 查看帖子 get 1 2 發布帖子 post 2 3 修改帖子 update 4 4 刪除帖子 delete 8
從上面兩張表的設計可以看出:采用二進制的方式少了一張中間表!!!它只多了兩個字段:一個是權限對應的權限值,一個是用戶擁有的權限值總和,所以可以知道——采用二進制的方式可以少一次表查詢。
按照一般方式(不采用二進制的方式)的做法是:
1.獲取當前請求的URL , 得到對應的權限對象(或 id)
2.查看當前用戶是否含有此權限
采用二進制的方式:獲取當前請求的URL ,得到對應的權限對象(權限值),用戶的權限值 & 當前權限的權限值
可以看出:采用二進制的方式少了一次表查詢。
如果嫌每次請求都要去數據庫中獲取對應的權限對象太過於麻煩,則可以把全部的權限放入本地緩存中,因為每個請求都會進行判斷,則可以視為熱點數據,則可以放入本地緩存,從而減少數據庫查詢。這個時候:
不采用二進制的方式:每次需要去用戶-權限對應表中判斷是否擁有相應的權限,嫌太麻煩,可以在user里面 用List<Integer> 裝入所有自己擁有的權限id
采用二進制的方式:可以直接從本地緩存中取出權限值,然后可用戶的權限值進行判斷
- | 一般方式 | 二進制方式 |
---|---|---|
空間 | List<Integer> | long |
時間 | O(n) | O(1) |
由上列表可以看出:不管是從時間還是空間來說,二進制的方式都占有明顯的優勢!!
二、 如何用二進制做權限
現在,可以知道用二進制的方式來做權限擁有明顯的優勢,那么具體的運算應該是怎樣的呢?
例子:用戶AA含有權限值 15 , 當前權限值 8 1. 判斷是否含有某個權限: 15 & 8 != 0 00001111 & 00001000 ——————----- 00001000 2. 添加權限: 15 | 8 = 15 、 15 | 16 = 31 00001111 00001111 | 00001000 00010000 —————------- -——————--- 00001111 00011111 3. 刪除某個權限 : 15 & (~8)= 7 00001111 ~ 11110111 ————------- 00000111
現在已經知道如何具體的進行運算,但是大家有沒有一個問題:一個整型32bit 所對應的權限是有限的,那么應該怎么做呢??是用long嗎??是個辦法,但是這個能夠一次性解決問題嗎??有沒有更好的辦法呢??答案是有的。
結合分級索引,可以這樣做:
可以看出:每個權限增加了一個權限位,用作分級,所以,現在唯一標識權限的可以用權限位和權限值來進行標識。那么這個時候:
public class User { private Integer userId; private String name; private String password; // 數組下標為0對應的值就為擁有權限位為0的權限值得總和 private long[] permissionSum; public boolean hasPermission(Permission permission){ int position = permission.getPosition(); long number = permission.getPermissionNum(); return !((permissionSum[position] & number) == 0); } }
每個用戶的權限值就需要用一個數組來存儲,其下標為0的對應着權限位為0的權限值和。這個是不是又轉換為數據結構的問題了,所以基礎很重要嘛~