当前位置: 技术问答>java相关
关于用户角色权限管理的探讨
来源: 互联网 发布时间:2015-04-29
本文导语: 用户权限管理是我们在做应用时经常遇到的问题,但是我们是不是曾经对它做过比较深入的分析呢?不同的系统中的用户管理是不是可以抽象出一个通用的模型呢? 我们先来看一个最普通的例子:论坛。 为了便于分析,论...
用户权限管理是我们在做应用时经常遇到的问题,但是我们是不是曾经对它做过比较深入的分析呢?不同的系统中的用户管理是不是可以抽象出一个通用的模型呢?
我们先来看一个最普通的例子:论坛。 为了便于分析,论坛的用户我们简化成三类:普通用户和版主。
以CSDN为例,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.
是不是表述了这个关系? 好吧,先到这里。
我要声明一点的是:这不是什么指南,也不是想告诉你如何如何做。只是我对这个问题的一些思考,我尽可能地把我纷繁的头绪整理出来,但是还是很乱,而且考虑得也很片面和肤浅,之所以拿出来,只是为了抛砖引玉,能引出您的真知灼见。
所以,我期待着你的参与,就上面的例子,或是你实际工作中的例子,给我们讲述一下你的思路。
欢迎致信yancheng@sharetop.com,我们一起讨论这个问题。
|
user表
USER_ID
其它属性
op表
USER_ID(foreign key)
BOARD_ID(foreign key)
其他属性
board表
BOARD_ID
BOARD_NAME
public class Operator {
int board_id;
其它属性
}
public class UserInfo {
String user_id;
Operator[] ops; //记录了user是那些版的版主
其他属性
}
public class UserDAO {
public static UserInfo getUserInfo(String user_id) throws SQLException {
从数据库中读取用户user_id的资料,返回UserInfo
}
}
public class UserAction {
String user_id;
int board_id;
int action_id;
}
public class csdn {
UserCache userCache = new UserCache();
public void login(String user_id) {
UserInfo userIndo = getUserInfo(user_id);
if( user_info!=null ) 把userInfo放到UserCache中
....
}
public void onAction(UserAction userAction) {
UserInfo userInfo = getUserInfoFromCache();
checkAction(userInfo,userAction);//对用户的操作进行权限检查
}
}
USER_ID
其它属性
op表
USER_ID(foreign key)
BOARD_ID(foreign key)
其他属性
board表
BOARD_ID
BOARD_NAME
public class Operator {
int board_id;
其它属性
}
public class UserInfo {
String user_id;
Operator[] ops; //记录了user是那些版的版主
其他属性
}
public class UserDAO {
public static UserInfo getUserInfo(String user_id) throws SQLException {
从数据库中读取用户user_id的资料,返回UserInfo
}
}
public class UserAction {
String user_id;
int board_id;
int action_id;
}
public class csdn {
UserCache userCache = new UserCache();
public void login(String user_id) {
UserInfo userIndo = getUserInfo(user_id);
if( user_info!=null ) 把userInfo放到UserCache中
....
}
public void onAction(UserAction userAction) {
UserInfo userInfo = getUserInfoFromCache();
checkAction(userInfo,userAction);//对用户的操作进行权限检查
}
}
|
权限的管理可以说在每个系统中都会遇到看了以上的大虾们的,忍不住想跟各位大虾们交流一下
在最近做的一个项目中(有关CRM)的我们对权限的管理思路如下:
我们使用了分组的形式,对于不同组有不同的权限,一个角色是属于一个组的。
在论坛中也可以用这种思路
版主是一个组OPER
interface OPER
{
定义基本权限(抽象级)
}
基本用户组User
interface USER
{
定义基本权限
}
注意以上都是interface因为我们对一个User组一个Oper组都不知道它们有一些什么权限(在论坛中表现为功能)也就是具体实现不管,它只定义了一个模具而已。以后其它的每一个论坛都实现这个模具,都跟这个样子差不多。
至于论坛中用户ID的来回切换
一个简单实用效的方法就是先把用户的在整个系统中的所有权限都一次性的读出来
保存为一个全局变量性的数据结构,这样不必在每次变换时都读数据库。这就是叫用内存换取速度。
以上只是个人思路,并没有具体的实现。
本人也正在做这方面的工作,写成一个通用的类库。现在这个类库差不多快完成,正在测试中!
欢迎大家来email:jiang_it@21cn.com讨论
在最近做的一个项目中(有关CRM)的我们对权限的管理思路如下:
我们使用了分组的形式,对于不同组有不同的权限,一个角色是属于一个组的。
在论坛中也可以用这种思路
版主是一个组OPER
interface OPER
{
定义基本权限(抽象级)
}
基本用户组User
interface USER
{
定义基本权限
}
注意以上都是interface因为我们对一个User组一个Oper组都不知道它们有一些什么权限(在论坛中表现为功能)也就是具体实现不管,它只定义了一个模具而已。以后其它的每一个论坛都实现这个模具,都跟这个样子差不多。
至于论坛中用户ID的来回切换
一个简单实用效的方法就是先把用户的在整个系统中的所有权限都一次性的读出来
保存为一个全局变量性的数据结构,这样不必在每次变换时都读数据库。这就是叫用内存换取速度。
以上只是个人思路,并没有具体的实现。
本人也正在做这方面的工作,写成一个通用的类库。现在这个类库差不多快完成,正在测试中!
欢迎大家来email:jiang_it@21cn.com讨论