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的权限值和。这个是不是又转换为数据结构的问题了,所以基础很重要嘛~