锁定老帖子 主题:用户权限控制分析
精华帖 (1) :: 良好帖 (1) :: 新手帖 (1) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2011-07-28
最后修改:2011-07-29
http://baike.baidu.com/view/73432.htm
我们都知道或听说过已经很成熟的RBAC模型,本文也不打算针对RBAC模型阐述更多,若在阅读本文时表示对RBAC模型完全不懂的话,可以到以下这个网址了解清楚:1.在RBAC模型中,有以下几个重要的概念
所谓的用户权限控制我想可以用这样一句简练的语句来表达吧:Who对What进行了How的操作。而很明显,Who就是用户,What就是资源,How就是操作了。 但是一般情况下用户是不会直接操作资源的,而是先成为了某种角色之后再操作资源,这时候社会更加有序。不然人人都可以对所有资源进行任何的操作,那就乱套了。 而往往,角色操作资源也是有限制的,也就是什么角色只能对什么资源作什么操作。这些也都是有规定的。 我想,这就是所谓的用户权限了吧。 既然有必要通过控制权限来维持秩序,那么权限控制必将是一门需要花心思研究的学问。 在我看来,如何抽象(或定义)“权限”是最重要的。 权限是什么?权限就是操作加上资源,在Web系统里来说,就是:权限 = Http方法+URL。 当然,这只是我的理解。 在设计的时候,需要理清RBAC模型中那些不同概念之间的关系。
下面,我尝试使用ER图和Java类图来表示它们的关系。
试试给出这样一个应用场景: 当前系统含有两个用户:UserA、UserB 两种角色:Role1、Role2 一个资源:News(新闻资源) 对资源的操作有:查看GET,添加POST,删除DELETE,修改PUT 标志资源的url格式: http://localhost/news http://localhost/news/new http://localhost/news/{id} http://localhost/news/{id}/edit 对以上的url添加上操作就能定义出一系列的权限: 查看新闻列表权限:http://localhost/news + GET 添加新闻内容权限:http://localhost/news + POST 查看某篇新闻内容权限:http://localhost/news/{id} + GET 删除谋篇新闻权限:http://localhost/news/{id} + DELETE 修改谋篇新闻内容:http://localhost/news/{id} + PUT 当然,上面定义权限的过程完全是可由开发者在系统设置里进行定义。这里要说明的是:不一定所有的url都需要被定义为权限,当系统需要对外提供url服务时,才有必要将其定义为权限。 你一定知道一个url没有被定义成权限时它根本就没法被使用。我们需要保证这点。例如我们可以通过隐藏菜单的方式来表达某个url没有被定义成权限。当然,这仅仅是其中一种方法而已。 试想一下: 我们使用管理员账号登陆系统后台,点击“权限管理”—>“新增权限”,然后页面显示一个url列表,我们选中某个url,然后分配操作(GET、POST、DELETE、PUT)给它,接着点击保存为"权限",这时候系统把我们选中的url加上操作保存到Permission表中,并告诉我们保存成功了。这时候我们就完成了一次权限定义的操作! 接下来,我们点击“角色管理”—>“分配权限”,页面将显示一个角色列表和一个权限列表,我们选中某个角色,然后再选中某个权限(当然可以选择多个权限),接着点击"保存",系统将为我们保存到RolePerm表,并通知我们成功了,这时候我们就完成了一次权限分配的操作。 最后,我们点击“用户管理”—>“分配角色”,有点类似以上的过程,总之最后我们肯定完成了一次分配角色给用户的操作。 以上的假想过程,就完成了一次用户权限管理操作了。这时候,使用某个被赋予某种角色的用户账号进行登录,点击任何一个按钮或者在浏览器中访问任何一个url,我想系统都应该会对其进行权限判断,从而保证系统的安全之类的。 其实我想表达的也就是这些了。我明白前面举的那些UserA、UserB后来没有被我使用,但是现实就是这样的嘛,不一定被写出来的都能够被用上的,对吧。 我想我也应该在这个时候结束这篇文章了。 哦,对了,好像忘记ER图了。不过没关系,我相信ITEye的各位都懂的。 纠结了几分钟,ER图和类图就免了,但是上一下Java代码吧。 这是我使用EWeb4j框架下的持久化类写法: Operation.java package simportal.persistent; import java.util.ArrayList; import java.util.List; import com.cfuture08.eweb4j.orm.config.annotation.Column; import com.cfuture08.eweb4j.orm.config.annotation.Id; import com.cfuture08.eweb4j.orm.config.annotation.Many; import com.cfuture08.eweb4j.orm.config.annotation.Table; import com.cfuture08.util.JsonConverter; /** * 操作,Http方法,包括GET、POST、DELETE、GET * @author weiwei[l.weiwei@163.com] * */ @Table("t_operation") public class Operation { @Id @Column("") private Integer id; @Column("") private String name; @Column("") private String description; //操作权限是一对多的关系 @Many(target=Permission.class,column="operationId") private List<Permission> permissions = new ArrayList<Permission>(); //省略setter&getter public String toString(){ return JsonConverter.convert(this); } } User.java package simportal.persistent; import java.util.ArrayList; import java.util.List; import com.cfuture08.eweb4j.orm.config.annotation.Column; import com.cfuture08.eweb4j.orm.config.annotation.Id; import com.cfuture08.eweb4j.orm.config.annotation.ManyMany; import com.cfuture08.eweb4j.orm.config.annotation.Table; import com.cfuture08.util.JsonConverter; /** * 用户-持久化对象 * * @author weiwei[l.weiwei@163.com] * */ @Table("t_user") public class User { @Id @Column("") private Integer id;// 自增长id @Column("") private String nickName;// 昵称 @Column("") private String account;// 账号 @Column("") private String password;// 密码,MD5加密 @Column("") private String status;// 用户状态:'在线'、'下线'、'锁定' @Column("") private String lastLoginTime;// 上一次登陆时间 @Column("") private String lastLoginIp;// 上一次登陆IP // 用户与角色时多对多关系 @ManyMany(target = Role.class, relTable = "t_user_role", from = "userId", to = "roleId") private List<Role> roles = new ArrayList<Role>();// 角色 //省略setter&getter public String toString() { return JsonConverter.convert(this); } } Role.java package simportal.persistent; import java.util.ArrayList; import java.util.List; import com.cfuture08.eweb4j.orm.config.annotation.Column; import com.cfuture08.eweb4j.orm.config.annotation.Id; import com.cfuture08.eweb4j.orm.config.annotation.ManyMany; import com.cfuture08.eweb4j.orm.config.annotation.Table; import com.cfuture08.util.JsonConverter; /** * 角色-持久化对象 * * @author weiwei[l.weiwei@163.com] * */ @Table("t_role") public class Role { @Id @Column("") private Integer id;//自增长ID @Column("") private String name;//角色名称 @Column("") private String description;//角色描述 //角色用户多对多关系 @ManyMany(target = User.class, relTable = "t_user_role", from = "roleId", to = "userId") private List<User> users = new ArrayList<User>();//用户 //角色权限多对多关系 @ManyMany(target=Permission.class,relTable="t_role_permission",from="roleId",to="permissionId") private List<Permission> permissions = new ArrayList<Permission>();//权限 //省略setter&getter public String toString(){ return JsonConverter.convert(this); } } Permission.java package simportal.persistent; import java.util.ArrayList; import java.util.List; import com.cfuture08.eweb4j.orm.config.annotation.Column; import com.cfuture08.eweb4j.orm.config.annotation.Id; import com.cfuture08.eweb4j.orm.config.annotation.ManyMany; import com.cfuture08.eweb4j.orm.config.annotation.One; import com.cfuture08.eweb4j.orm.config.annotation.Table; import com.cfuture08.util.JsonConverter; /** * 权限-持久化对象 * * @author weiwei[l.weiwei@163.com] * */ @Table("t_permission") public class Permission { @Id @Column("") private Integer id;// 自增长ID private String name;// 权限名称 @Column("") private String url;// http访问url @Column("") private String description;// 描述 //权限和操作时多对一的关系,一种操作可以形成多种权限(搭配不同的url) //暂时来说操作目前有GET、POST、PUT、DELETE四种操作,不排除未来会有其他操作 @One(column = "operationId") private Operation operation;// 操作 // 角色权限是多对多关系 @ManyMany(target = Role.class, relTable = "t_role_permission", from = "permissionId", to = "roleId") private List<Role> roles = new ArrayList<Role>();// 角色 //省略setter&getter public String toString() { return JsonConverter.convert(this); } } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-07-28
RBAC乃是分页杀手,请问楼主对此有啥看法?
|
|
返回顶楼 | |
发表时间:2011-07-28
最后修改:2011-07-28
PetriNet 写道 RBAC乃是分页杀手,请问楼主对此有啥看法?
这个怎么解释?分页传递的,无非是: 搜索条件 + 下页页码,有时候也传递一下总记录数,免得下页再count一下, 传递方式通常是:
另外,分页有时候还需要先查询n条记录缓存,然后分页跳转时如果是缓存内记录,就不去查数据库了; 另外,分页有时候需要回显上n页,或者浏览过的下n页的记录的checkbox。 新手会直接把选中的checkbox存到session中,老手一般采用分页参数,传递记录checkbox 的ID和 选中状态组成的json字符串。 另外,分页如果有导出excel的功能,一般要做到导出excel的查询参数和分页参数共用,但是导出excel的查询条件又不能是分页查询。 ---------------------------------------------------------- 综上这些,是分页经常遇到的问题。 这些和RBAC没有什么关系吧?求解? |
|
返回顶楼 | |
发表时间:2011-07-29
PetriNet 写道 RBAC乃是分页杀手,请问楼主对此有啥看法?
问题一:如何做到不同用户,看到的列表数据不一样?这个才是难点。 如果直接在列表页做权限判断,太死板,一旦权限或角色读取数据的范围定义发生改变,列表页代码就需要重写; 问题二:如何做到不同用户,看到的列表页列是不同的?这个也是难点。 比如A用户能够看到:员工姓名、员工性别两列; B用户能够看到:员工姓名、员工性别、员工学历、员工月薪。 如何实现以上,如果在列表页做硬编码判断,同样面临问题一的困扰。 |
|
返回顶楼 | |
发表时间:2011-07-29
george_space 写道 PetriNet 写道 RBAC乃是分页杀手,请问楼主对此有啥看法?
问题一:如何做到不同用户,看到的列表数据不一样?这个才是难点。 如果直接在列表页做权限判断,太死板,一旦权限或角色读取数据的范围定义发生改变,列表页代码就需要重写; 问题二:如何做到不同用户,看到的列表页列是不同的?这个也是难点。 比如A用户能够看到:员工姓名、员工性别两列; B用户能够看到:员工姓名、员工性别、员工学历、员工月薪。 如何实现以上,如果在列表页做硬编码判断,同样面临问题一的困扰。 你这个应该细分到数据权限级别了,楼主说的大概意思是功能级别权限 |
|
返回顶楼 | |
发表时间:2011-07-29
george_space 写道 PetriNet 写道 RBAC乃是分页杀手,请问楼主对此有啥看法?
问题一:如何做到不同用户,看到的列表数据不一样?这个才是难点。 如果直接在列表页做权限判断,太死板,一旦权限或角色读取数据的范围定义发生改变,列表页代码就需要重写; 问题二:如何做到不同用户,看到的列表页列是不同的?这个也是难点。 比如A用户能够看到:员工姓名、员工性别两列; B用户能够看到:员工姓名、员工性别、员工学历、员工月薪。 如何实现以上,如果在列表页做硬编码判断,同样面临问题一的困扰。 问题一: 唯一的解决办法只有通过sql传参,要做到扩展性很强,那么可以为系统提供一个权限数据控制的功能。比如现在有订单列表,那么可以在它下面设定访问权限,比如部门级别,公司级别 等等各种查看级别,每个级别配一个sql字符串。比如我要看到部门级别的,那么可以设定 #order.createUser.departmentId=$session.deptId。在后台有个解释引擎,把#和$分别替换成数据库表和session中的值。并且传入到你的dao,返回列表。这样做的好处是你的访问权限可以无限扩展,想要什么数据都可以(因为参数的变化是你自己定的) 问题二:也比较好解决,权限下面加个字段权限。前台编写一个判断字段权限的tag就搞定了。 |
|
返回顶楼 | |
发表时间:2011-07-29
icanfly 写道 george_space 写道 PetriNet 写道 RBAC乃是分页杀手,请问楼主对此有啥看法?
问题一:如何做到不同用户,看到的列表数据不一样?这个才是难点。 如果直接在列表页做权限判断,太死板,一旦权限或角色读取数据的范围定义发生改变,列表页代码就需要重写; 问题二:如何做到不同用户,看到的列表页列是不同的?这个也是难点。 比如A用户能够看到:员工姓名、员工性别两列; B用户能够看到:员工姓名、员工性别、员工学历、员工月薪。 如何实现以上,如果在列表页做硬编码判断,同样面临问题一的困扰。 你这个应该细分到数据权限级别了,楼主说的大概意思是功能级别权限 功能和数据不能分着看 ( 做程序的时候可以抽象成不同 也便于自己理解 ) 实际中权限大多数与数据有关。单独的功能权限根本不需要讨论。 |
|
返回顶楼 | |
发表时间:2011-07-29
==》 我觉得权限和操作应该是多对多的关系 一个权限可以包含多种操作 同时一个操作亦可分配给多种权限 |
|
返回顶楼 | |
发表时间:2011-07-29
LZ描述的还有欠缺
用户-角色-权限,这一块没什么异议了,结构清晰。 当然,还有更复杂的“用户-机构-角色-权限”,这个就不详细说了。 问题在于“资源”,我们怎么定义资源?? URL只是资源的一种,不是全部,而且是属于比较好控制的资源。 还有很多其他形式的资源,例如: 正如其他人说的,数据也是资源,要做到行/列权限控制,那是长久以来做业务系统的难点。 页面元素,你可能要控制某个按钮某些人才能看。 还有很多很多。。。“资源”,都是可能在系统中需要控制权限的 |
|
返回顶楼 | |
发表时间:2011-07-29
george_space 写道 PetriNet 写道 RBAC乃是分页杀手,请问楼主对此有啥看法?
这个怎么解释?分页传递的,无非是: 搜索条件 + 下页页码,有时候也传递一下总记录数,免得下页再count一下, 传递方式通常是:
另外,分页有时候还需要先查询n条记录缓存,然后分页跳转时如果是缓存内记录,就不去查数据库了; 另外,分页有时候需要回显上n页,或者浏览过的下n页的记录的checkbox。 新手会直接把选中的checkbox存到session中,老手一般采用分页参数,传递记录checkbox 的ID和 选中状态组成的json字符串。 另外,分页如果有导出excel的功能,一般要做到导出excel的查询参数和分页参数共用,但是导出excel的查询条件又不能是分页查询。 ---------------------------------------------------------- 综上这些,是分页经常遇到的问题。 这些和RBAC没有什么关系吧?求解? ________________________________________________________________________ 在加上对应数据权限的功能就能很好的解决 这个问题 |
|
返回顶楼 | |