由django的Authentication想到RBAC permission兑现(Struts版)
先简单的说一下RBAC的概念:
主要有user, role 和 permission 这么三个元素,用户可以有多个角色,角色可以有多个 permission,这里的permission可以理解成对resource+operation。在这个基础上还可以增加group的概念,相信不难理解。
假设系统对于权限的要求是:能控制系统动态菜单的访问,控制其它的提交操作。
以下的想法是基于struts 1.2 + java 1.4 (部门最近一直比较保守,晕死)
个人觉得要实现这个模型的难点在于permission, 也就是其中的resource,应用中的资源应该包括动态资源和静态资源两类:
- 动态资源:比如说用户上传的文档,不同的文档可能会给不同的角色分配不同的权限。
- 静态资源:某些地方系统只要求控制固定的表单哪些角色可以查看,哪些角色可以更新。
个人认为动态资源相对比较容易处理,因为我们把这个动态资源的ID作为resource的ID,然后再加入一个resource type的属性就可以处理多种资源了。为什么说动态资源比较容易处理呢,因为我们处理这些动态资源的时候都是以ID来操作的,查询的时候也可以用ID进行过滤,唯一需要注意的可能会是性能问题,可以考虑使用缓存。
静态资源就有点麻烦了,说白了,其实控制静态资源就是控制角色是否可以访问系统中的某个方法。那么这时候我们怎么在permission里标识这个操作呢?有人会说,用URL,偶承认,这个绝对可以控制,但是这么做的话也就意味着权限是无法交给真正的用户来维护,除非你愿意维护一个URL与用户能看懂文字的对应列表,有点麻烦,也很难检查有没有遗漏的。
我想的办法是在自己的BaseFom(一般用struts的多少都会有这个东东)中加入一些代码:
public BaseForm extends .... { ... public static final String CREATE = "create"; public static final String REVIEW = "view"; public static final String UPDATE = "update"; public static final String DELETE = "delete"; private List operations = new ArrayList(); //用于获取对用户友好的表单名称 public String getFormName() { return ""; } public List getOperations() { //map中的value也是考虑用户友好 operations.add(new HashMap().put(CREATE, "Add new record")); operations.add(new HashMap().put(REVIEW, "Search and view the records")); operations.add(new HashMap().put(UPDATE, "Update the record")); operations.add(new HashMap().put(DELETE, "Delete the record")); return operations; } ... }
接下来假设说我们有一个针对项目的新增/删除/修改/查询以及失效 操作,先来写我们的form:
public class ProjectForm extends BaseForm { private static final String FORMNAME = "Project Information"; public static final String INVALID = "invalid"; public String getFormName() { return FORMNAME; } public List getOperations() { List operations = supper.getOperations(); operations.add(new HashMap().put(INVALID, "Set project as invalid")); return operations; } ... }
再来看看action怎么写:
public ProjectAction extends ... { //创建项目,其它的方法类似 public ActionForward createProject(....) { //从session中获取用户信息 User user = ....; //假设我们有一个类专门处理权限检查 PermissionChecker checker = new PermissionChecker(); checker.setResource(form.getFormName()); checker.check(user, ProjectForm.CREATE); } }
上面action中的方法比较丑陋, 其实如果是JDK 5的话就可以用annotation解决就比较优雅了,而且上面的代码还可以进一步重构。
个人认为这么做相对URL的控制有几个好处:
- 很容易就检查到方法是否控制到;
- 方法名的修改不影响用户的配置;
- 在一个地方配置整个系统的URL很容易让人晕菜;
- 不同的模块可以分开指定有哪些需要的操作,比如说其它的模块还需要发布、撤销等操作;
其不足之处就是代码量有所增加,action的方法还需要自己调用一些API,如果用URL控制就可以用filter了。
系统部署后也可以遍历所有配置的form,根据现有的信息就可以配置出想要的permission并授权给角色。
写了半天没提到一点django,呵呵,其实django中自带的 Authentication 模块就是类似的做法,模块自带了CRUD的操作,用户可以在写自己的model的时候增加定制的操作,有兴趣的朋友可以参考这里: http://docs.djangoproject.com/en/dev/topics/auth/#topics-auth
想法是今天回家的路上想的,肯定有很多没考虑到的地方,不知道这种方式是否实际可操作,随后也打算学习一些acegi的原理。