二進(jìn)制與權(quán)限控制有什么關(guān)系
最近在做權(quán)限方面的設(shè)計(jì),很自然想到了二進(jìn)制的方案。
二進(jìn)制跟權(quán)限有什么關(guān)系?舉兩個(gè)例子大家就熟悉了:
- Linux的文件權(quán)限: 可執(zhí)行 001, 可寫(xiě) 010,可讀100,三種權(quán)限都有是111,也就是int(7)。
- PHP的錯(cuò)誤級(jí)別:E_ERROR = 00...001, E_WARNING = 00...010, E_PARSE = 00...100。
優(yōu)勢(shì)在哪里
那么為什么要用二進(jìn)制來(lái)控制權(quán)限呢?我覺(jué)得最明顯的優(yōu)勢(shì)有兩個(gè):
1.易存儲(chǔ)易擴(kuò)展
假如現(xiàn)在要設(shè)計(jì)一個(gè)用戶權(quán)限系統(tǒng),分別有CRUD四種權(quán)限。那么最先想到的可能是在數(shù)據(jù)表里建4個(gè)tinyint字段,如下:
| USSE_ID | C_ABLE | R_ABLE | U_ABLE | D_ABLE |
|---|---|---|---|---|
| 12345 | 0 | 1 | 1 | 1 |
| 67890 | 1 | 1 | 1 | 1 |
這樣設(shè)計(jì)用是能用,但如果以后要新增一個(gè)權(quán)限類型,例如是發(fā)郵件的權(quán)限。那必須改數(shù)據(jù)表,新增一個(gè)EMAIL_ABLE字段。這樣的需求對(duì)此方案來(lái)說(shuō)是很恐怖的,基本不可擴(kuò)展。
我們換個(gè)思路,改為用二進(jìn)制來(lái)控制權(quán)限,那么只需要一個(gè)字段就足夠了。
首先把所有權(quán)限用二進(jìn)制定義好:
C_ABLE: 1 << 0 (int 1)
R_ABLE: 1 << 1 (int 2)
U_ABLE: 1 << 2 (int 4)
D_ABLE: 1 << 3 (int 8)
EMAIL_ABLE: 1 << 4 (int 16)
因?yàn)槎M(jìn)制也就是一個(gè)整數(shù),所以用一個(gè)int/longint字段就可以把所有權(quán)限表示出來(lái)。數(shù)據(jù)表設(shè)計(jì)如下:
| USER_ID | PERMISSONS |
|---|---|
| 12345 | 7 |
| 67890 | 15 |
以后如要新增權(quán)限類型,定義好它的二進(jìn)制即可,不需要新增數(shù)據(jù)表字段,擴(kuò)展性大大增強(qiáng)。
2.權(quán)限控制簡(jiǎn)單
權(quán)限增減
相信大家對(duì)PHP的error_reporting()函數(shù)很熟悉。
新增錯(cuò)誤上報(bào)項(xiàng),用|符號(hào),例如:
error_reporting(E_ERROR | E_WARNING)
表示上報(bào)E_ERROR + E_WARNING。
刪除錯(cuò)誤上報(bào)項(xiàng),用& ~,例如:
error_reporting(E_ALL & ~E_NOTICE)
表示在所有上報(bào)項(xiàng)中排除E_NOTICE。
所以只需要用三個(gè)位運(yùn)算符就可以實(shí)現(xiàn)權(quán)限的增減控制,實(shí)現(xiàn)復(fù)雜權(quán)限的疊加。
權(quán)限判斷
最后一個(gè)問(wèn)題來(lái)了。在二進(jìn)制方案里,用戶的最終權(quán)限是一個(gè)整數(shù),那如何判斷該用戶是否擁有某個(gè)權(quán)限呢?
我們先看看PHP的error_reporting機(jī)制是怎么做的,見(jiàn)源碼:
https://github.com/php/php-src/blob/PHP-5.4.1/Zend/zend.c#L1080

留意#1080行,其實(shí)就是用了一個(gè)
&運(yùn)算符。假設(shè)用戶設(shè)置如下:
error_reporting(E_NOTICE | E_WARNING)。判斷用戶是否需要上報(bào)某種類型錯(cuò)誤,執(zhí)行以下運(yùn)算即可:
//需要上報(bào)
if((E_NOTICE | E_WARNING) & E_NOTICE) //為true,表示需要上報(bào)E_NOTICE,即擁有某權(quán)限
//不需要上報(bào)
if(!((E_NOTICE | E_WARNING) & E_ERROR)) //為false,表示不需要上報(bào)E_ERROR,即沒(méi)有某權(quán)限
至于為什么要用&運(yùn)算,大家把它們的二進(jìn)制寫(xiě)出來(lái)算一遍就一目了然了,這里不作展開(kāi)。
結(jié)論
用二進(jìn)制來(lái)做權(quán)限控制是一個(gè)非常經(jīng)典的方案,也有廣泛的使用場(chǎng)景,大家多加參考。