UML软件工程组织

应用系统权限表示的算法技巧
应用软件系统权限问题的另类解决方法

权限管理确是个很麻烦的问题
例如,一个系统中,有N个模块,每个模块有添加,删除,修改,完全控制,列表等权限
有N个用户,现在的问题是,通常,我们会建立一个数据表来表示权限分配,这个数据表的一般形式是
用户ID 添加 删除 修改 完全控制
3 0 1 1 3

这样一般来说没什么问题,突然有一天,你需要有一种新权限例如叫做运行,这时候,问题就来了,你得更改数据表,添加对应的字段呀,不光如此,

程序中的逻辑也有问题了

当然,也有更聪明的兄弟使用另一种设计方法,就是包含式权限设计,类似WINDOWS系统中,每一种权限,将包含前一种权限,例如
读取是最基本的,运行包含读取,列表包含运行,修改包含列表以此类推,用数字表示就是
读取 1
列表 2
运行 3
修改 4
删除 5
....
以此类推,这样,就将第一种方法的扩展问题解决掉了,当有新的权限时,在应用程序中,为其指定一个新的权值就可以了,在实际判断权限中,例如 ,
判断有无修改权,就比较用户权限值是不是大于或等于4,就可以了
听起来不错,似乎解决了问题,但是,再想想看,不对,这个设计的确使权限的扩展性强了,可是,也使权限体系的灵活性降低了,为什么呢?假如,有

一个用户,我想给他读取,修改两种权限,而不想给他运行和列表权限,这下怎么办呢?这个设计是等级制的权限,权限具有自高往低的包含,因此,

根本无法实现这种自由组合

难道真的没有办法了吗?
NO!
每一种权限,相当于一种开关,也就是一个BIT,假如我们将这些权限,每一种权限用一位表示,那么,整个权限体系,不过是个BIT序列而已,也就是

说不过是二进制的011010而已,这样一来,就好解决问题了
同样的问题,我们这样假设:

第一位 表示 读取
第二位 表示 运行
第三位 表示 修改
第四位 表示 删除
......
以此类推,假如,我们想给一个用户分配运行和修改权限,其他权限都不给予,于是,他的权限字符串可以表示为:0110,这样,用若干个位的组合,我 们就解决了权限的灵活组合问题(位组合这种解决方案,真是比比皆是,其实,他就是一种数据结构而已,我们叫他UNION?)
另外一方面呢?如何扩展?扩展就更容易了,当添加新的权限时,可以直接在高位加上一位即可
如何判断有无某权限呢?我们可以直接取某位的值,根据其0,一来判断即可
最后,实际上,C/C++/C#中,你可以做得更棒,那就是定义一个枚举类型,例如:
enum rights
{
read,
list,
exec,
write,
delete
}

这样的好处是用名字来引用权限,就更好了
判断权限时,可以使用按位与,例如,用户权限为100001
而运行需要的权限是1000
两者按位与的结果是00000000,显然无此权限,用公式表示就是
RN & R=RN,则代表用户有此权限,我的意思是
所需权限&用户权限=所需权限 符合此条件,即表明有权限
写的比较抽象,但是,相信有权限设计经验的朋友一看就明白了
我也是在最近设计一个系统时,被客户的变化逼的不行,后来,受<编程珠玑>一书中第一章关于对100万个电话号码进行排序的文章中受到的启发
BW:C#中的UNION结构原来是取消了-:(

# re: 应用系统权限表示的算法技巧
在刚出来工作的时候,我写过一个系统就是用这样的方式来表示权限,
后来慢慢权限控制变得复杂,开始感觉到可能有问题,
最后,得感谢我当时做的系统规模很小,刚刚够用,不然在权限控制这一模块就要重写了。

# re: 应用系统权限表示的算法技巧
这种方式比较老了,N年前我就已经用过了。“位域”这种技术在汇编及C++中应用非常普遍。在C#中的BitArray和BitVector32就是为解决位域问题的。其实权限控制的实现难度并不在如何表示某种权限并在需要判断的地方准确判断出来。真正的难度其一是在于判断过程,如何将判断权限的过程集中起来而不是过于分散从而增加维护的难度(AOP是一种好的方式,但是现在通过的AOP方案还不够成熟,性能也有很大的问题);其二在于权限决不仅仅是增删改查这么简单的bit,而是需要与域中的对象契合(例如业务数据所在的区域、方面、类别),并且不能丧失已有的可扩充性并加入权限控制的维度。

# re: 应用系统权限表示的算法技巧
简单的权限还可以,复杂的就不行了。

# re: 应用系统权限表示的算法技巧

当然,也有更聪明的兄弟使用另一种设计方法,就是包含式权限设计,类似WINDOWS系统中,每一种权限,将包含前一种权限,例如
读取是最基本的,运行包含读取,列表包含运行,修改包含列表以此类推,用数字表示就是
读取 1
列表 2
运行 3
修改 4
删除 5
....
以此类推,这样,就将第一种方法的扩展问题解决掉了,当有新的权限时,在应用程序中,为其指定一个新的权值就可以了,在实际判断权限中,例如

,判断有无修改权,就比较用户权限值是不是大于或等于4,就可以了
听起来不错,似乎解决了问题,但是,再想想看,不对,这个设计的确使权限的扩展性强了,可是,也使权限体系的灵活性降低了,为什么呢?假如,有

一个用户,我想给他读取,修改两种权限,而不想给他运行和列表权限,这下怎么办呢?这个设计是等级制的权限,权限具有自高往低的包含,因此,

根本无法实现这种自由组合


假如用“存在”来代替“大于或等于”的判断呢?

# re: 应用系统权限表示的算法技巧
我想在权限设计方面,linux的用户权限管理是个不错的注意

简单说明:
读取 1
列表 2
运行 4
修改 8
。。。

读取和列表:1+2=3
读取和运行:1+4=5
读取和列表和运行:1+2+4=7
读取和列表和修改和运行:1+2+4+8=15

。。。。。
只判断他们的总和,3就表示读取和列表权限,15就表示读取和列表和修改和运行。。。然后再写个enum ,列出各自的具体权限

# re: 应用系统权限表示的算法技巧
@雁儿飞飞
这个适合于权限比较固定的情况吧?
如果要添加另一个权限就比较麻烦了吧?

# re: 应用系统权限表示的算法技巧
@菩提树
呵呵,当然得去要去找元数
这个比较简单啊,只要最后一个数是前面所有数值加1就可以

读取 1
列表 2
运行 4
修改 8

这个删除 16(添加) 就是1+2+4+8=15

在加1
这样就不会出现您的

1+16=17
4+13=17

条条大道同罗马吗

# re: 应用系统权限表示的算法技巧
@雁儿飞飞
你说的跟我说的是一回事
LINUX中用的就是我说的这种方法,只不过,它表示的更明显,不信你看
1=2的0次方
2=2的1次方
4=2的2次方
8=2的3次方
....
依次类推,就是我说的位移法
另外,你说的加法,确切来说,应该是按位或
我说出了位,但是,没说按位的方法计算权限值

# re: 应用系统权限表示的算法技巧
@双鱼座
其实我探讨的不是如何在代码中去判断权限以及如何赋权的问题
我要说的是如何将表示权限以利于存储和扩展,因为毕竟你要将权限持久化
而持久化的问题在于当权限种类是不固定的时候,扩展起来就不容易,因为要面临加字段的问题
但是用位域的方法表示,只需一个字段,权限无论多少,都只需用一个字段表示,从而以不变应万变
希望听到大家有更好的解决方法

# re: 应用系统权限表示的算法技巧
在Cuyahoga项目中,看到他的角色判断也是这样的
if ((this.PermissionLevel & (int)accesLevel) == (int)accesLevel)
{
....
}

按位与来判断的

# re: 应用系统权限表示的算法技巧
如果要考虑到对表单中的某些字段的权限控制,有没有什么好的办法?

我目前是采用固定的方式,字段有变化时就需要重新设置权限,扩展性很差。

# re: 应用系统权限表示的算法技巧
@雁儿飞飞
菩提树 说的这个算法中根本不会出现你说的
1+16=17
4+13=17
这种情况的,因为13根本不在2^n 里面,
呵呵呵,你还没有理解他的算法
你说的算法和他说的算法其实原理都是一样的

所有的权限都用2^n 来表示
任何不同的权限相加都不会有重复
因为2^n 的二进制表示,永远是一个1跟着n个0

# re: 应用系统权限表示的算法技巧
不用怀疑这种算法的可行性,
我之前的那家公司在很多项目中都有用到这个算法
虽然这个算法并不稀奇
可是如果真的是
菩提树
自己想出来,
我还真的是很佩服兄台,
当年我们公司用这套权限算法的时候
还是一个数学博士给弄出来的,呵呵呵

# re: 应用系统权限表示的算法技巧
@WilliamsQi:
@雁儿飞飞:
我晕倒!我已经告诉你们这个叫位域了,在Win32 API中到处都用到。在C#中将enum加上FlagsAttribute标签就自动实现位域了,这是什么新技术吗?

# re: 应用系统权限表示的算法技巧
@菩提树:
用“位域”表示权限还是有局限性的。一般在一个系统中权限是通盘考虑的,以保证不同的权限间合理的覆盖关系。一般在数据库中可以用整型(最多64位)来表示,这样就意味着权限的枚举不超过64个。而事实上在一个小系统中,64位往往都是不够的。于是,专门有一个表来表示权限标识,用另外一个表来表示权限与用户的关联这种看似笨拙的方法却体现出更大的可扩展优势来。当某个权限需要与数据(就象Cancer711说的)进一步关联的时候,可以在第二张表中加入控制数据。这种方式是我从2003年起用的通用方式。由于权限太多可能连你自己都记不起哪个权限是哪个权限了。所以,我表示权限采用“权柄”的方式,有些类似命名空间形式。例如:SysManager.UserManager.List、SysManager.UserManager.Add、SysManager.UserManager.Remove、SysManager.UserManager.Disable......由于每一个权限都有一个注释,所以很容易运行时获得一棵权限树,从而可以根据CheckBox的状态来确定是否赋予某个权限。

# re: 应用系统权限表示的算法技巧
@双鱼座
能否将你的方法详细道来
BTW:我说的,跟你说的并不冲突,我们两个的方式结合起来
就是Cuyahoga项目中使用的权限体系统,我昨晚看它的代码,体会到我的想法是不完整的,但是看了你的写法,两者结合,正是我要的
另外,cuyahoga确实是个好项目,里面有许多值得吸收的东西

# re: 应用系统权限表示的算法技巧
@williamsqi
要是谁告诉你这个算法只有博士能想出来,你可千万别相信
这个权限的算法,在我想出来前,我的确不知道LINUX的权限原理,曾疑惑很久,开始,之所以后来想出来,是因为看到了一个更难的例子:给100万个电话号码排序.这是编程珠玑第一章的一个算法技巧例子,他就是独辟蹊径,它就是使用类似的算法

# re: 应用系统权限表示的算法技巧
在有限权限设置中,用位域是非常好的手段。如DOS/Linux系统的文件权限设置。
但是对于模块众多,每个模块都有自已的特殊权限控制,并且模块还可以被扩充(比如功能扩充,插件等)的情况下,用位域实现是比较困难的。

BTW:
首先,位组合在C/C++中是结构位域,而不是联合(union)。
其次,C#中不是取消了联合,只是对联合做了限制——引用类型不能被联合。
C#中是使用 Offset 属性来控制结构中的联合字段的。

版权所有:UML软件工程组织