用户权限管理是我们在做应用时经常遇到的问题,但是我们是不是曾经对它做过比较深入的分析呢?不同的系统中的用户管理是不是可以抽象出一个通用的模型呢?
我们先来看一个最普通的例子:论坛。 为了便于分析,论坛的用户我们简化成三类:普通用户和版主。
以为例,sharetop是java版的版主,所以他登录的过程是这样:
登录(普通用户)->进入Java版(版主)->进入C++版(普通用户)->回到Java版(版主)
从现象上分析,在切换不同页面的时候,角色发生了改变,是吧?对应上面的过程,在sharetop登录后用RID来记录他的当前角色。 那它的变化就是
User->Sysop->User->Sysop。
于是,我们要解决的问题是:如何决定这个RID的变化?
if( BlockID is java && my.name is sharetop ) my.RID=sysop; else my.RID=user;
那第二个问题是:这个IF语句是如何产生出来的?
我们在数据库中建立这样一个表role_table:
UID RID BID
sharetop sysop java
sharetop user c++
于是,我们根据这个表的内容去生成这个判断语句,上面的代码改成:
select RID from role_table where BID=current.BlockID and UID=my.name; my.RID= selected RID;
我们再优化一下,其它普通用户实在没必要记录,所以上表只需记录是sysop的用户的UID和BID即可。 命名为bolck_sysop_table
UID RID BID
sharetop sysop java
yanchang sysop jsp
(*)注意:我给出的表都只是逻辑结构并非真正的物理结构:),比如这两个字段可能被放在block_table中,而不是用一个独立的表来保存。下同。
RID可以保存在session中或是用有状态会话Bean来处理,只在每个版区的入口页面加入角色变化的验证代码,后面的页面即使用session中的RID值。同样,如果加上帖主的角色,唯一的区别在于验证代码应存在于显示帖子的页面,使用RID的页面也只有显示帖子的页面了。
在这个例子中我们看到这么一个现象:普通用户覆盖了全部的版区,在这个大集合里有一些子集,如java版版主、C++版版主等等。每个版主都是普通用户这个超类的某个子类,是具有特殊权限的普通用户。
问题是不是得到了解决?看起来好象是的:) OK,那我们来看另一个复杂一些的例子吧。
我们有一个非常简单的电子政务系统,假如只有一部分:信访室和检查室。
我们的用户只有四类:信访室主任、信访室办信人、检查室主任、检查室办案人。
首先,我们来看一下它的权限分配(假设我是局长我修改了规则):
办信人A处理信访件xf001,那另一个办信人B就不能对xf001进行操作包括看都不行。但是信访室主任C可以看任何信访件,但是不能修改。xf001交到检查室,由D承办,他也可以看xf001的内容但不能修改,检查室主任E看不到任何xf001的内容,但E可以看xf001中的由D新增加的内容。
是不是有点复杂了?:)那么我们上面分析的模型是不是能拿来用呢?想想,有点问题。
本例中,信访室和检查室是两个相对独立的集合,所以我们先分开来考虑:
在信访室内部,对于xf001,A有操作权,C有观看权,B没有权限。对应表的role_xf_table如下:
UID RID BID
A oper xf001
B oper xf002
于是我们这样分配他们的权限:
select RID from role_xf_table where (BID=current.xfNo or RID=leader) and UID=my.name; my.RID=selected RID;
if( my.RID==oper) permit operate.
if( my.RID==leader) permit view.
单纯的信访室内部,这样似乎没问题了,但是上面的权限分配中提到xf001交到检查室后,由D承办,他也可以看xf001的内容。于是上面的模型肯定要修改。
在这里,我们需要再引入一个“组”的概念。
信访室是一个组,在这个组内,我们可以用上面分析的模型对它的权限管理进行控制,同样,检查室也是一个组,组内的权限管理也可以套用我们上面的模型。但是组与组之间的权限管理呢?
D属于检查组,他却对信访室组中的xf001的内容有观看权,我们要修改上面的表,加入组GID,如下表role_group_table:
UID RID GID BID
A oper xf xf001
B oper xf xf002
C leader xf
D oper jc aj001=xf001
E leader jc
* aj001是另一个操作对象,它与xf001之间有关联。也正是有这个关联,我们允许操作aj001的D有权限看xf001的内容。为了简化分析,我们把aj001等同于xf001。
我们的语句是:
select RID,GID from role_group_table where (BID=current.blockID or RID=leader) and UID=my.name;
f( oper of xf ) permit operate.
if( oper of jc ) permit view.
if( leader of xf ) permit view.
是不是有那么点道理?好,我们还可以对这个再做一下优化,比如这三个条件判断,不一定要写死在程序中,我们应该为它建立一个“RID与GID权限组合分配表”,字段为:RID、GID、Authority。
其实,在这里的RID也是另一个组的概念:oper组与leader组。我们可以为leader赋于一个共同的权限(比如收某种级别的公文等)。
其实这个模型还有很多要进一步讨论的地方,不过它大体上解决了问题,我们先放一放,再来看另一个模型吧。
第三个例子是:一个广告管理系统。用户可以分为三种:系统管理员、广告管理员和广告主用户。
系统管理员负责广告管理员的新增和删除。广告管理员只负责定向与数个广告主联系,并为他们提供广告业务。而广告主是我们的客户,每个广告主可能有几个广告。广告管理员只能看、修改自己负责的广告主的资料和此广告主交付的广告的资料。而广告主也只能看自己的广告的统计资料。
这里的权限比较简单,我们注意到这样的语汇:只能……自己……。所以这里的权限是一棵树。
如果我们继续沿用上面的分析,并且在这里也试着引入组的概念,如果每个广告管理员算为一个组,那么这个组里又可以分为每个广告主的一个小组?是不是这样?所以,这里的组有一种嵌套的关系,这也正是树的结构。如果不把它叫做组,这里的权限其实就是具有分级分支的关系。
我们再回过头来看一下上面的例子,在第二个例子中,组似乎有交叉的关系?如果RID与GID都被称为组,那它们之间的确是交叉的。 再看第一个例子,比较发现,如果把广告系统看成一个论坛,广告主相当于帖主,广告管理员就是板主,那么,唯一的区别在于每个帖主只能在自己特定的版区活动,java的兄弟不能去C++玩,这样看来,其实广告系统的权限才是最简单的。
与论坛系统对照而言,广告主也可以看作是广告管理员的一个子集,但是不是为他增加了某些权限,相反是削减了某些权限。
好象是这么一回事。这样一来,我们也可以用论坛使用的那一套数据表结构来描述它了。把BID改为PID,分别用role_table记录广告主与广告管理员的权限和ad_table记录广告与广告主的关系:
UID RID PID
a oper a
b oper b
c adver a
d adver a
e adver b
ID OWN
1 c
2 c
3 d
4 e
5 e
6 e
语句这样:
select role.PID from role_table,ad_table where role_table.UID=ad_table.OWN and ad_table.ID=current.ID;
if( selected role.PID is my.name ) permit operate.
else forbid.
是不是表述了这个关系? 好吧,先到这里。
我要声明一点的是:这不是什么指南,也不是想告诉你如何如何做。只是我对这个问题的一些思考,我尽可能地把我纷繁的头绪整理出来,但是还是很乱,而且考虑得也很片面和肤浅,之所以拿出来,只是为了抛砖引玉,能引出您的真知灼见。
所以,我期待着你的参与,就上面的例子,或是你实际工作中的例子,给我们讲述一下你的思路。