基于组件方法级别授权访问
板桥里人 https://www.jdon.com 2005/10/29(转载请保留)
当用户登陆进入系统后,面临着其访问权限的检查,目前实际应用中,授权访问(ACL)一般有以下几种:
- 1. Web资源,主要是以Web
URL为特征,对Web资源的Jsp/Servlet 图片等目录下全部资源实现授权访问。
- 2. 组件资源,同一个组件的不同方法实现对资源进行不同性质的操作,每种操作方法都需要实现授权访问,例如:A角色可以实现帖子创建,但不能实现帖子删除。
- 3. 数据资源,保存在数据库中的某些数据只能让特定角色一些访问。比如某个帖子只能让积分超过多少的用户访问。
其中Web资源的ACL实现有很多种,使用基于Web容器的安全验证或使用开源项目JGuard。Web资源的ACL的有一个特点,它只能对Web资源实现授权访问,如果你设置/auth目录下的资源只能被某些角色访问,也就是说网址http://localhost/myweb/auth/下包括图片等任何资源只能被指定角色访问,这样,对Web资源形成了有效的保护。
假设你有一个组件服务,如TestService如下:
public interface TestService { public UserTest initUser(); public void createUser(EventModel em); public void updateUser(EventModel em); public void deleteUser(EventModel em); public UserTest getUser(String userId); public PageIterator getAllUsers(int start, int count); |
如果你授权目录和未授权目录都有对TestService访问,如下面struts-config.xml
<action name="userActionForm" path="/userSaveAction" type="com.jdon.strutsutil.ModelSaveAction" scope="request" validate="true" input="/user.jsp"> <forward name="success" path="/result.jsp" /> <forward name="failure" path="/result.jsp" /> </action> <action name="userActionForm" path="/auth/userSaveAction" type="com.jdon.strutsutil.ModelSaveAction" scope="request" validate="true" input="/auth/user.jsp"> <forward name="success" path="/auth/result.jsp" /> <forward name="failure" path="/auth/result.jsp" /> </action> |
也就是说,网址http://localhost/myweb/userSaveAction.do与http://localhost/myweb/auth/userSaveAction.do
都是实现用户资料的保存,都将分别调用TestService的createUser和updateUser等方法。如下:
如果你的设计目的是:用户的创建可以开放给任何人,但是用户资料的修改或删除只能由超级管理员Admin实施,其他任何人员都没有权限进行修改或删除。
为了实施这个设计目的,如果只使用 Web资源的ACL,使用如上图设计,将存在安全隐患,因为http://localhost/myweb/userSaveAction.do是完全开放给任何人,任何人对将从这个入口实现对TestService的访问,这样系统容易留有安全漏洞。
一个解决方案是将用户资料的修改或删除功能从TestService接口中分离出来,这是破坏了对象的封装性。
根本解决之道是使用基于组件方法的ACL,也就是前面总结的第2条,下面我们谈谈在Jdon框架下如何实现这个功能。
实现组件TestService的第一步思路是在其createUser和updateUser等方法中加入权限判断,这种方式缺点是将权限和业务逻辑(如数据库操作)混淆的在一起,授权在发生变化时,修改createUser方法有可能更改业务逻辑代码,造成很大的不安全性。很显然,ACL和业务逻辑必须分开。
分离的方法首先使用设计模式,这里可以选用Decorator模式或Proxy模式,所以我们需要实现TestService接口的一个子类TestServiceProxy,在TestServiceProxy中的各个方法中进行权限检查,而原来的TestService子类实现TestServiceImp则完全封装的是是业务逻辑。jive就是采取这个实现原理的。
但是这样做哟一个缺点,每个接口都要实现一个Proxy类,如果有很多服务接口,需要实现很多Proxy类,这些Proxy类数量多而庞杂,系统不简练。
比较好的解决办法是使用拦截器概念来实现,也就是我们只要做一个通用的Proxy类,系统能够在运行时自动检查。
使用Jdon框架可以很方便地实现这个目的,因为Jdon框架的组件都是可插拔,可配置的,因此只要我们编制一个权限拦截器插入Jdon框架,然后使用XML配置文件描述一下哪些方法只能被哪些角色访问,权限拦截器根据这个配置规则进行运行检验,这样既做到分离,又达到简洁的效果。
首先,我们使用XML定义ACL的访问规则,这个规则XML可以由我们任意指定,因为是我们自己写XML解析器解析它,我把它设定如下格式:
<permission> <service ref="testService"> <method name="createUser"> <role>Admin</role> </method> <method name="updateUser"> <role>Admin</role> <role>User</role> </method> <method name="deleteUser"> <role>Admin</role> </method> </service> </permission> |
在这个配置中,我们定义了TestService的createUser方法只能被Admin访问,而updateUser的方法只能被Admin和user访问。
然后,我们自己做一个XML解析器解析它,使用Jdon框架辅助的Jdom读取XML很方便,拷贝框架内其他读取XML代码即可,这个类在本文附件源码包中的PermissionXmlParser。
最后,我们编制一个拦截器,主要代码如下:
public class PermissionInterceptor implements MethodInterceptor
{ boolean hasPerm = false; //获得Jdon框架的SessionContext,这个类似HttpSession //使用SessionContext作为缓存,不必每次根据Principle Name查询角色时, //从XML配置中检查当前服务和方法以及角色是否在配置中,也就是是否有权限。 if (hasPerm) } |
当然为了使这个拦截器能够被Jdon框架装载,有两个方法:一个是更改jdonframework.jar包中META-INF目录下的aspect.xml,将PermissionInterceptor放入;或者在自己项目下建立一个文件myaspect.xml,在其中配置如下:
<aspect>
<interceptor name="test_permissionInterceptor" class="com.jdon.framework.test.service.PermissionInterceptor" pointcut="pojoServices" /> |
只要确保myaspect.xml在WEB-INF/classes下,Jdon框架就会自动装载,当然因为PermissionInterceptor使用到XML解析,还需要在jdonframework.xml配置解析器PermissionXmlParser如下:
<pojoService name="testService" class="com.jdon.framework.test.service.TestServicePOJOImp"/> <pojoService name="operatorDao" class="com.jdon.framework.test.dao.OperatorDao" > <constructor value="java:/NewsDS"/> </pojoService> <pojoService name="permissionXmlParser" class="com.jdon.framework.test.service.PermissionXmlParser" > |
其中test_permission.xml就是前面的权限定义XML配置文件。
注意,上述使用Jdon框架上述基于组件方法的ACL前提是,必须首先获得验证合格的登陆用户信息,本例子中登陆系统是使用基于Web容器的登陆方法。