基于二进制位运算的权限控制

本文主要介绍如何基于二进制位运算(2的幂指数)实现精细化权限控制,将多个具体权限排列组合成一个整体权限进行使用。

Linux文件权限控制

在Linux中,通过chmod命令修改文件或目录的操作权限是经常用到的功能操作,例如

                
                    chmod 666 xxx  (每个人都有读和写的权限)
                    chmod 777 xxx  (每个人都有读和写以及执行的权限)
                
            

这里的数字777或666的每一位分别代表一种用户类型(文件所有者、群组用户、其他用户)的操作权限。 (如果感兴趣,可以深入研究下Linux的相关知识.)

单个6或7是怎么来的,代表什么意义呢?

实际上这里的6/7是三种具体的权限计算(相加)得出的权限组合值,文件或目录的具体权限分为3种:只读、只写、可执行

权限 权限数值 二进制 具体作用
r 4 00000100 read,读取。当前用户可以读取文件内容,当前用户可以浏览目录
w 2 00000010 write,写入。当前用户可以新增或修改文件内容,当前用户可以删除、移动目录或目录内文件
x 1 00000001 execute,执行。当前用户可以执行文件,当前用户可以进入目录

依照上面的表格,权限组合就是对应权限值求和,如下:

                
                    7 = 4 + 2 + 1           读/写/运行权限
                    6 = 4 + 2               读/写权限
                    5 = 4 + 1               读/运行权限
                    4 = 4                   只读权限
                
            

这样大家就明白666/777这些数字的意义了,实际上是通过组合得到的值。那为什么权限数值用的是1/2/4,而不是其他数字呢?

实现原理

首先看下为什么权限数值是1/2/4这样有规律的数值,而不是其他的数值?

其实,这里用的数值实际上是二进制,而1/2/4只是二进制转化为十进制后的结果。

                
                    int('00000001',2) = 1   # 2^0 执行
                    int('00000010',2) = 2   # 2^1 写
                    int('00000100',2) = 4   # 2^2 读
                
            

对比二进制就好理解了,其实是每一个标志位代表了一个权限,如果有权限对应位就是1,否则就是0。

                
                    7 = int('00000111',2)   # 有3个标志位的权限 读+写+执行
                
            

如果想要检查权限组合值是否具有某个具体权限,可以通过二进制的位运算&进行判断。

相同位的两个数字都为1,则为1;若有一个不为1,则为0

                
                    7:  0 0 0 0 0 1 1 1
                    2:  0 0 0 0 0 0 1 0
                    ————————————————————
                    2:  0 0 0 0 0 0 1 0

                    7&1 = 1
                    7&2 = 2
                    7&4 = 4
                
            

这样我们就明白了使用2的幂指数(1/2/4/8/16/...)进行权限控制的原理,实际上是通过二进制的标志位进行的控制

应用权限控制

在应用系统中,权限控制是最基本的功能,对于资源的访问一般都要进行权限(增/删/改/查)控制,可以将一个角色的各种权限用多条记录分别记录下来,不过稍显繁琐, 那我们是不是也可以借鉴Linux的实现方式,通过类似数字组合的方式进行权限控制呢?

接下来我们通过一个基于角色的权限控制应用案例,看一下实现思路(以下代码基于Python+SQLAlchemy)

                
                    # 操作权限类型常量
                    class Permission:
                        ADD = 1         # 2^0
                        DELETE = 2      # 2^1
                        UPDATE = 4      # 2^2
                        QUERY = 8       # 2^3

                    # 角色
                    class Role(Base):
                        __tablename__ = 'sys_roles'

                        id = Column(Integer, primary_key=True, autoincrement=True)
                        name = Column(String(32), unique=True, nullable=False)
                        permissions = Column(Integer, nullable=False)

                        def add_permission(self, permission):
                            if not self.has_permission(permission):
                                self.permissions += permission

                        def remove_permission(self, permission):
                            if self.has_permission(permission):
                                self.permissions -= permission

                        def reset_permission(self):
                            self.permissions = 0

                        def has_permission(self, permission):
                            return self.permissions & permission == permission  # 位运算,判断是否有权限

                    # 用户,权限由其对应的角色进行控制
                    class User(Base, ModelMixin):
                        # ...
                        role_id = Column(Integer, ForeignKey('sys_roles.id'), nullable=False)
                        role = relationship('Role')

                        def can(self, permission):
                            return self.role is not None and self.role.has_permission(permission)

                    #以下为测试代码
                    >>> role = Role()
                    >>> role.add_permission(Permission.ADD)
                    >>> role.add_permission(Permission.QUERY)

                    >>> role.has_permission(Permission.QUERY)  # True

                    >>> user = User()
                    >>> user.role = role
                    >>> user.can(Permission.QUERY)  # True
                    >>> user.can(Permission.DELETE)  # False
                
            

这样我们就基于二进制运算实现了权限控制,不过具体的应用会更复杂,可能还会包括菜单权限等,但是实现思路大致相同。