1、概述

管理系统、论坛或者一般网站的后台都需要做权限的控制,根据业务不同实现的方式也不尽相同。似乎常用的大概有两大类(因为没做过详尽调查,所以也可能不止这些)通过为每个权限资源指定用户标识控制(以下简称资源标识法)和通过位运算来控制,其中前者为通常做法。复杂的软件也有权限和业务逻辑分开的独立权限管理系统,@吉日嘎拉的通用权限管理系统就属于这种吧。因系统架构不同权限控制方式也不相同,如C/S架构只需要控制客户端的菜单就可以做到对权限的控制,而C/S架构下出了控制菜单外还需逐个去控制每个菜单对应资源的权限,打个比喻,当年项羽和刘邦推翻清王朝后前者并未称帝而是自封为楚霸王,都城选在了地势开阔的彭城,把刘邦整到的关隘多易守难攻的汉中,后来刘邦在韩信等人谋划下明修栈道暗度陈仓,终于拿下了关中。如果把刘邦和项羽当成是两类资源,那么刘就是基于C/S的,其他各方要想访问(进攻)刘,必须通过秦岭这一屏障,别无它路,因此刘可以在函谷关设立一个检查的机构(统一登录认证),只有合法的用户才可进入关中;相对而言项就是基于B/S的开放系统,敌人可以从四面八方“访问”项羽,即使项并未公布哪些路可以通往彭城。总之C/S只能通过特定入口进入系统并按开发人员事先设计好的流程进行交互,而C/S的页面是开放的,不可能通过“隐蔽入口”的方式来控制权限。现在我要探讨的是另外一种模式的权限控制方式,在这之前先简述下上述这两种权限控制方式。

2、常用权限控制方式

2.1资源标识法

为每一个权限资源(如一个按钮或者一个菜单项)指定哪些用户可以访问,结构如下表:

权限资源

可以访问的用户

发帖

张三、李四、王五

模块管理

阿九、狗剩

系统设置

小白

……

……

对应到数据库的话,这张表的数据条数n≈权限资源数*可以访问这些资源的用户数,很明显这样会浪费很多存储空间。

       一个较明智的做法是不直接为用户分配权限,而是通过把先把权限分配到某一个组,然后将用户加入指定组中,见以下两张表:

权限资源

组编号

组名称

发帖

A

普通用户

模块设置

B

超级版主

系统设置

C

系统管理员

 

组编号

可以访问的用户

A

张三、李四、王五

B

阿九、狗剩

C

小白

 

 

这样做显然会让权限表的数据量减少很多,n≈权限资源数*组数。

2.2位运算控制法

每一个数位表示一个权限资源,然后用二进制的1表示具有该权限,0表示没有该权限。

如果权限值如下图

 

权限系统功能架构图 权限管理系统原理_权限控制

即001:普通用户

   010:超级版主

   100:系统管理员

由以上可以得知,每一个单独的权限资源(如超级版主)只可能在某一个数位上出现1,其它数位全为0,如果把这样的数转换为十进制,它就是2的n-1次方(n为从左往右的二进制数位)。

对于拥有多个权限,则只需将相应的数位换成1,其它数位保持为0即可,如同时拥有超级版主和系统管理员权限则表示为110,即十进制的3。

总结起来,加权限 p总=p1|p2,(其中p总为最终权限,p1为原权限,p1、p2既可以为单一权限资源也可以为复合权限资源,下同),减权限,p总=p1&(~p2),判断具有某一权限p0的实体是否能访问需要pn权限的资源只需判断p0&pn==p0,如果结果为true则能访问。

位运算控制法的好处显而易见,将每个权限资源和用户的对应整合成了一个整数,不但节省存储空间,操作起来也比较灵活,但是它也有致命的局限性,那是因为它天生的本质所决定的,即用二进制没一会数位表示一个权限,对于一个Int64类型的整数来说,最多能表示31个权限,如果独立权限资源项多余31个就不能用这种方法。

 

简单介绍了上边两种权限控制方式后,再来看看我现在要谈论的另外一种权限控制方式吧,初衷是为解决前段时间工作中遇到的问题,然后在@Cris Doff帮助下想出来的一种方式,因所学有限,可能会有很多不对的地方,请大家斧正。

3、核心内容——类位运算控制法简析

       之所以称为类位运算控制法,是因为借鉴了前者的思想,但是不再用整数来表示权限,而是用一串只包含0和1的字符。具体做法是在全县资源表中为每一个权限资源设定一个唯一索引值来标识该权限资源,然后在和用户或者权限组关联的表中使用字符串表示该实体拥有的所有权限,具体例子见下表

 

 

 

 

 

表一 权限表

 

权限系统功能架构图 权限管理系统原理_位运算_02

表二  角色表

 

权限系统功能架构图 权限管理系统原理_位运算_03

表二中powers字段表示该角色拥有的权限,其中每一个1均表示拥有对应于表一的索引(power)的权限。

这样的话再多的权限资源都可以采用简单方式满足(sql server中 varchar类型的最大长度可以为8000)。

这种方式下的权限操作也比较简单,加权限,只需将对应索引的值设为1即可,

即 powers[i]=’1’。同样,减权限只需将对应索引值设为0即可,即powers[i]=’0’。判断是否具有某个权限,只需判断对应索引的值是否为1,即powers[i]==’1’。

       最后说明一下,在这种方式下,权限(即表二中的powers)的长度为所有独立权限资源数的总和,如果不拥有某一权限,一般应将该位置志为0,无论是在中间还是在两边。另外,当权限资源的总数改变时,也应该同时改变powers的长度。