readme
https://blog.csdn.net/zzzgd_666/article/details/100709605
最近在自己搭建一个管理后台,到了权限角色的时候,按常规涉及了以下几张表:
也是相当常见的模型了。
但是随机我发现一个不大不小的问题,当权限表/资源表稍微大一点的时候,角色关联权限tbl_role_permission 就会有大量数据
这才一个角色,很伤脑筋。于是我想到了上个项目中用到的二进制表示权限方法。
表设计
二话不说,看表
我们知道一个long类型,可以转为64位的二进制类型,也就是说,0L就是
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
如果把每一个位置bit_pos表示一个权限,除去最左边的符号位以外,可以表示63个权限或资源,也就是一条tbl_role_permission 关联记录就可以表示63中不同权限。
其实按正常系统来说,63中权限、资源是肯定不够的,这时候就用bit_group来区分,超过63则划入另一个group。
也就是通过group和bit_pos来确定一个权限。
那么tbl_role_permission 就会是这样:
只是可读性变差了一些,但是关联看起来更简洁了
角色权限的增删
现在角色和权限通过一个二进制来关联,那么怎么给一个角色添加权限和删除权限呢?
增加
使用二进制的位运算,比如 现在一个角色的权限是 bit_group是 0, bit_str是00001111,要加入一个 bit_group是 0,bit_pos是5的权限 (这里bit_pos是从0开始,为了符合程序的索引下标规则)
规则如下
原二进制位权限 | 要添加的权限二进制位
首先得到权限的bit_str: 用到位运算的左移运算
1 << bit_pos
1
1 << 5 = 00100000
再和00001111 进行或运算
00100000
| 00001111 = 00101111
1
2
删除
删除权限也很简单,还是上面的00101111,如果要把刚刚的bit_group是 0,bit_pos是5的权限删掉
还是先拿到二进制位
1 << 5 = 00100000
然后
原二进制位权限 & (~ 要删除的权限二进制位)
也就是
00101111 & (~ 00100000)
~ 00100000 也就是 11011111
再进行与运算
00101111
& 11011111 = 00001111
1
2
判断是否具有权限
为什么采用二进制来表示权限,还有一个便利就是可以简单的判断这个权限属不属于这个角色的权限集合
如果一个角色的权限集合是 00001111,判断bit_pos是2的是不是这个权限集合:
原二进制权限集合 & ( 要判断的权限二进制 ) == 要判断的权限二进制
1 << 2 = 00000100
boolean b =
00001111
& 00000100
== 00000100
1
2
3
4
很明显判断结果为true
合并权限
要合并两个权限,不用担心重复的问题了,直接与运算
原权限 | 新权限
查询sql
说了这么多,怎么连表查询角色对应的权限集合呢?
根据角色名查权限
SELECT
*
FROM
tbl_role r
LEFT JOIN tbl_role_permission rp ON r.id = rp.role_id
LEFT JOIN tbl_permission p ON p.bit_group = rp.permission_bit_group
WHERE
CONV(rp.permission_bit_str,2,10) >> p.bit_pos & 1 = 1
and r.role_name = ?
;
1
2
3
4
5
6
7
8
9
10
这个where条件其实就是将bit_str的权限集合,比如00001111,右移bit_pos,和1进行与运算,其实还是判断bit_str在第bit_pos个索引位置是不是1,是则有这个索引位置的权限,否则没有
介绍两个mysql方法
conv():
CONV(N,from_base,to_base) 表示转换进制, N是列名或值, from_base是从什么进制,to_base是转到什么进制
CONV(rp.permission_bit_str,2,10) 就是从二进制的01串变成十进制的数
BIN():
BIN(N) ,N是列名或值,将该值从10进制转成二进制,但是前面的0会忽略
LPAD():
LPAD(N,len,s),N是列名或值,len是总长度,s是当N长度不够len时,使用s来填充左边补全长度
所以如果我们想得到某个十进制的二进制位,
根据权限名查询角色
其实差不多
SELECT
*
FROM
tbl_permission p
LEFT JOIN tbl_role_permission rp ON p.bit_group = rp.permission_bit_group
LEFT JOIN tbl_role r ON r.id = rp.role_id
WHERE
CONV(rp.permission_bit_str,2,10) >> p.bit_pos & 1 = 1
and p.name
= ?
1
2
3
4
5
6
7
8
9
10
总结
总之用二进制来代表权限,一个明显的好处就是原本63条数据库记录都可以用一条记录代替。而且判断用户权限的时候,不用从几十上百条权限里,一个个遍历判断进行鉴权。只需要进行一个二进制位运算就可以了。直接位运算一般都是比平时的代码要高效的。
缺点,表查询更复杂了,原本直接关联表id查询就可以,现在需要进行各种转换和运算。而且这个设计在工作交接的时候,还得需要详细交代bit_group和bit_pos是什么,有什么用,代码上的可读性也受影响了,容易看懵
————————————————
版权声明:本文为CSDN博主「zzzgd_666」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zzzgd_666/article/details/100709605