基于二进制控制系统权限

简介: 在数学和数字电路中指以2为基数的记数系统,以2为基数代表系统是二进位制的。这一系统中,通常用两个不同的数字0和1来表示。在计算机中,最常用的是二进制,因为组成计算机系统的逻辑电路通常只有开和关这两个状态,用0和1很好表示这两种状态。

二进制是什么?

在数学和数字电路中指以2为基数的记数系统,以2为基数代表系统是二进位制的。这一系统中,通常用两个不同的数字0和1来表示。在计算机中,最常用的是二进制,因为组成计算机系统的逻辑电路通常只有开和关这两个状态,用0和1很好表示这两种状态。

二进制的使用

二进制可以控制系统的权限,说的简单点,即通过二进制0/1的位置来控制权限,因为某个功能要么有权限,要么没有权限

例如有一个场景:用户登录某个客户端(平板、手机、网页),要通过客户端的来源判定他是否有访问权限,我们可以通过二进制的权限位置,从最后一位往前(0位、1位、2位),设置3种权限:

可以访问平板:00000001
可以访问手机:00000010
可以访问网页:00000100

假设我们配置用户user1,可以通过平板和网页访问,即用二进制表示为00000101,用十进制表示为:5

当用户使用手机登录时,获取应用的权限00000010(一般每个应用调用登录接口都会携带一个appKey,这个appKey绑定了它所属的权限,根据appKey可查到对应的权限),十进制为:2,那怎么判定用户是否可以登录到当前应用呢?

通过&运算,功能权限 & 用户拥有的权限 == 功能权限?
即:2 & 5,转换为二进制:

  00000010
& 00000101
-------------
  00000000

&运算,只有两个位都是1,对应位才是1,
只有当2&5 == 2时,表示当前用户有手机登录权限。

二进制存储的优点

我们设计一个权限系统,一般都会基于RBAC模型,即用户-角色-权限,表设计如下:
image.png

常规做法,用户通过一个角色关联多个权限,数据存储如下:
image.png

这仅仅是一个角色,如果一个系统需要关联的角色很多,那么这张表将会存储大量的数据

通过使用二进存储可以大大减少数据量的存储,我们知道JAVA中Long类型可以转换为64位二进制,

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

如果把每一个位置来表示一个权限,除去最左边的符号位以外,可以表示63种权限,即一个角色最多可以关联63种权限,如果一个角色关联了63种权限,那么我们只需要插入一条数据即可,但在复杂的系统中,63种权限肯定是不够的,可以通过设计一个权限空间来进一步区分,比如最初权限空间为0可以表示63种,当超过63种权限,权限空间递增,然后再可以表示63种权限,所以我们对之前的角色、权限表进行改造:
image.png

角色-权限新增/删除

当用户在界面上新增,或删除权限,我们又该如何更新二进制权限的数据?

增加权限

比如有一个角色,对应的权限空间为0,权限二进制是00001111,需要添加一个,权限空间为0,二进制为:00100000,二进制位置为5,使用二进制的“|”运算符,规则如下:

原二进制权限 | 要添加的二进制权限

通过二进制位得到要添加的二进制权限

1 << 5 = 00100000
再和00001111 进行或运算

  00100000
| 00001111
-----------------
  00101111

00101111即为最新的权限

删除

还是以上面新增为例,00101111需要删除添加的权限00100000。
原则:

原二进制权限 & (~要删除的二进制权限)
也就是:
00101111 & (~00100000)
~00100000 表示取反,得到11011111,然后和原二进制做&操作

  11011111
& 00001111
-----------------
  00001111

判断用户是否具有权限
判断用户权限同上面用户通过客户端访问的例子,原则:

功能权限 & 用户拥有的功能权限 == 功能权限

通过角色查询权限

数据库表都存储的是二进制数据,我们如何通过一个角色查询到它所有的权限呢?
先上SQL:

SELECT
*
FROM
 角色表 r
LEFT JOIN 角色-权限关联表 rp ON r.角色ID = rp.角色ID
LEFT JOIN 权限表 p ON p.权限空间 = rp.权限空间
WHERE
CONV(rp.权限二进制,2,10) >> p.权限二进制位 & 1 = 1
and r.角色名称 = ?;

重点介绍CONV函数:

CONV(N,from_base,to_base) 表示转换进制, N是列名或值, from_base是从什么进制,to_base是转到什么进制
CONV(rp.权限二进制,2,10) 就是从二进制的01串变成十进制的数

比如:权限二进制为00001111,通过CONV转换为十进制得到16
有一条权限数据为:00001000,他的二进制权限位置为3,将 16 >> 3 得到 00000001,即将所在的权限位置移动到了最后一位,只要和1做 & 操作,得到的结果如果是1,那么表示包含了这条权限数据

根据权限查角色

SELECT
*
FROM
权限表 p
LEFT JOIN 角色-权限关联表 rp ON p.权限空间 = rp.权限空间
LEFT JOIN 角色表 r ON r.角色Id = rp.角色Id
WHERE
CONV(rp.权限二进制,2,10) >> p.权限二进制位 & 1 = 1
and p.权限名称 = ?

通过二进制存储数据的好处,大大降低了数据量,原来63条数据可以只用一条数据就可以代替,进行权限判断时,原来需要从几十甚至上百的数据一条一条判断,现在仅仅需要进行位运算就可以了,位运算一般比前面的逻辑高效的多。
但也有缺点,数据库存储的都是二进制,可读性降低了,如果项目交接给其它同事,还得对于权限空间和权限位置这些字段含义详细介绍,并且查询使用到了各种函数转换,这些字段的索引也失效了
总结:一般对于用户权限功能个数确定,比如一个供应商有几十万门店,用户、角色、权限数量不多,可以考虑使用二进制来控制权限提升效率、减少存储数据量。

本文来源于公众号《百川分享会》:baichuanshare

image.png

目录
相关文章
|
NoSQL 关系型数据库 MySQL
泛微Ecology9+Emobile7部署
泛微OA的平台化,相比之下,的确是很不错,为方便公司内部考勤,加班审批,报销等流程,这边采用泛微的E9
6993 0
泛微Ecology9+Emobile7部署
|
缓存
IDEA找不到或无法加载主类
IDEA找不到或无法加载主类
4912 0
IDEA找不到或无法加载主类
|
数据采集 Java Linux
Java“Broken Pipe”解决
Java中遇到“Broken Pipe”错误通常是因为Socket连接被远程主机关闭,而本地程序仍在尝试写入数据。解决方法包括:1. 检查网络连接和防火墙设置;2. 增加超时设置;3. 使用异常处理捕获并重试。
3505 4
|
9月前
|
运维 Ubuntu 关系型数据库
MySQL包安装 -- Debian系列(Apt资源库安装MySQL)
本文介绍了在Debian系列系统(如Ubuntu、Debian 11/12)中通过APT仓库安装MySQL 8.0和8.4版本的完整步骤,涵盖添加官方源、配置国内镜像、安装服务及初始化设置,并验证运行状态,适用于各类Linux运维场景。
2714 0
MySQL包安装 -- Debian系列(Apt资源库安装MySQL)
|
SQL 存储 关系型数据库
第一篇:数据库基础与概念
这篇文档面向数据库初学者,系统介绍了数据库的基础概念、类型、管理工具及实践方法。内容涵盖数据库定义、应用场景(如电商、银行系统)、数据库管理系统(DBMS)的功能与常见系统(MySQL、PostgreSQL等),以及关系型与非关系型数据库的区别。同时,文章详细解析了基本术语(表、记录、字段、主键、外键)和ER图设计,并提供了实践建议,包括创建简单数据库、学习SQL语言、使用管理工具等。最后推荐了学习资源和书籍,鼓励读者通过实际项目巩固知识,逐步掌握数据库的核心技能。
1715 11
|
自然语言处理 Prometheus 监控
基于DeepSeek的智能客服系统实战:从开发到部署
本文详细介绍如何将基于DeepSeek的智能客服系统从开发到部署,涵盖服务器选择、环境配置、代码部署及Web服务器设置。通过具体案例和代码示例,讲解系统上线步骤,并介绍使用Prometheus、Grafana等工具进行性能监控的方法。此外,针对高并发、API调用失败等常见问题提供解决方案,确保系统的稳定运行。最后强调数据安全与隐私保护的重要性,帮助读者全面掌握智能客服系统的部署与维护。
|
Web App开发 缓存 JavaScript
一次性完整学完搭建PWA项目
一次性完整学完搭建PWA项目
1184 0
|
存储 安全 API
PicGo配置阿里云oss
PicGo配置阿里云oss
742 0
|
关系型数据库 数据库 数据安全/隐私保护
springboot+dynamic-datasource多数据源配置动态切换
springboot+dynamic-datasource多数据源配置动态切换
6310 0
|
JavaScript 索引
js【详解】arr.splice() 数组拼接
js【详解】arr.splice() 数组拼接
1205 0