关于用户角色权限管理的探讨

时间:2021-07-27 08:20:30
用户权限管理是我们在做应用时经常遇到的问题,但是我们是不是曾经对它做过比较深入的分析呢?不同的系统中的用户管理是不是可以抽象出一个通用的模型呢?    
   
  我们先来看一个最普通的例子:论坛。   为了便于分析,论坛的用户我们简化成三类:普通用户和版主。    
   
  以为例,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.    
   
  是不是表述了这个关系?   好吧,先到这里。    
   
  我要声明一点的是:这不是什么指南,也不是想告诉你如何如何做。只是我对这个问题的一些思考,我尽可能地把我纷繁的头绪整理出来,但是还是很乱,而且考虑得也很片面和肤浅,之所以拿出来,只是为了抛砖引玉,能引出您的真知灼见。    
   
  所以,我期待着你的参与,就上面的例子,或是你实际工作中的例子,给我们讲述一下你的思路。