系统权限判定
有时,系统需要做权限时,在数据库中,我们可能会简单设计“用户”表、“操作项”表及“用户操作项分录”表,需要检查某个操作权限时,只需在“用户操作项分录”表中查询即可。
下面介绍另两种简单的算法,可以不需要中间表“用户操作项分录”。两种简单权限算法(一)
两种简单权限算法(二)
1:先来看一组数字:
2^2+2^4+2^5=52
52 &(2^2)=52 & 4= 4 =2^2
52 & (2^5)=52 &32= 32 =2^5
52 & (2^3)=52 &8= 0 !=2^3
52 & (2^1)=52 &2= 0 != 2^1
注:此处“^”为次方,不是异或。
从数字中可以发现,52是2^2、2^4、2^5的“和”,而有趣的是,拿这个“和”(即52)分别与2^2、2^4或2^5做“逻辑与”运算时,得到的值还是2^2、2^4或2^5。而52与其它数做逻辑与运算时,得到的值则不会与其相等。
正是这种现象(数学不好,不知道这种现象叫什么),我们可以把“操作项”表中的每个操作项都添加一个数字编码,如:
操作A-->1
操作B-->2
操作C-->3
操作B-->4
操作B-->5
假设某个用户包含:操作A和操作C的权限,则可以在用户实体中记录他的权限为2^1+2^3=10,其中1和3是操作项对应的编码。当需要检查此用户是否包含某此权限时,则可以直接用逻辑与运算,而不需再查数据库中的中间表。
如:检查是否包含操作X(对应的编码为N)的权限
-
int v= 2^N;
-
If( 10 & v ==v){
-
return true;
-
} else{
-
return false;
-
}
其实关键点在逻辑与的算法上,根据几个基本公式:
0 & A=0
1 & A=A
A & A=A
可以得到,任意值A,&A后,结果还是A。
上面例子中的10,二进制为1010。1010就是2^1+2^3之和,没有出现进位,事实上,如果没有出同两个(或以上)的次方之和(底为2),则不可能出现进位,因为2的N次方的值永远都是1后面N个0。事实上2的N次方,只上左移N位而已。
那么:
10 & 2^1=10 & 2-->1010 & 0010 =0010
1010
& 0010
--------------
0010
10 & 2^4=10^16-->1010&10000=0 != 10000 !-->2^4
01010
& 10000
----------------
00000
所以,只是数字游戏而已。
最后补充下:
1:这种算法最大的优势是判断权限的速度快,底层只是位移,另一方面也可以多个权限一起验证,如 10 & (2^1+2^3)= 2^1+2^3,10 & (2^1+2^2) != 2^1+2^2。
2:上面提到一个前提条件:“如果没有出同两个(或以上)的次方之和(底为2)”,即同一个权限编码值,不能添加两次。当然,这只有在代码层面有bug才会出现。
3:操作项数量有限:java中,int占4字节,32位,只有32个操作项,long的话,才64个操作项。当然,可以用将权限分模块,每个模块分配几十上操作项是够用的。或用byte[],则可以不限数量了。
JAVA例子:
-
public static void main(String[] args) {
-
int x[]={ 1, 2, 3, 4, 5, 6, 7}; //权限id
-
//总的权限,即有3和6的权限(2^3+2^6)
-
int cx=( int)Math.pow( 2, 3)+( int)Math.pow( 2, 6);
-
int c=( int)Math.pow( 2, 6); //要检查的权限
-
int v = cx & c; //结果(cx & 2^?)
-
System.out.println(c==v); //如果相同,则有此权限