struts2的CRUD中的权限控制初探(转)
在信息管理系统中,涉及到最多的就是对信息(数据)的增删改查,当然在真是的系统中,对于这些操作要控制在严格的权限中,在前面的文章中,我们已经使用 struts2+hibernate+spring实现了简单的crud操作,现在我们就来研究下如何给这些相关操作增加权限控制。
当然最简单的实现就是在每个Action的CRUD方法中首先判断登陆用户的权限,如果没有此方法操作的权限就可以抛出一个说明原因的异常,这样在一个庞大的系统中,这样类似权限检查的代码就会在众多Action中蔓延,这将会使得业务逻辑代码变得晦涩,对系统以后的维护变得困难,严重违反了单一职责原则。
相反,我们可以将一个权限控制剥离到一个代理中,这样Action几乎不需要改动就可以达到权限控制的目的。以下是详细实现。
1.基本的实现步骤:使用一个动态代理类(Proxy),通过拦截一个对象(我们实现crud操作的一个抽象类AbstractCRUDAction)的行为("load", "store","remove")并添加我 们需要的功能(权限控制)来完成。Java中的java.lang.reflect.Proxy类和 java.lang.reflect.InvocationHandler接口为我们实现动态代理类提供了一个方案.
2.实现InvocationHandler接口的invoke()方法,所有代理对象的调用都会发送到invoke方法,所以只要在调用前后实现自己的控制即可,这里我们在调用代理方法之前进行权限控制,伪代码如下:
本例的代码如下:
3.调用代理类实现系统功能,代码大致如此:
这样我们的权限控制目的就在引入动态代理后优雅的实现了,在上面的代码大家可能已经留意到,权限控制是在实际操作之前完成的,也有很多操作需要在 invoke之后完成,比如用户在完成一笔交易以后,积分要相应增加....那么我们就要在动态代理类的invoke后加入doAfter()之类的代码。是不是感觉到些什么,对,AOP,就是AOP思想的起源。
最后回到我们需要解决的问题上,由于我们是对struts2的Action进行动态代理,在整个struts的整个框架中,我们并不显式调用 action,这可怎么办呢?呵呵,不用着急,在aop风靡的今天,struts不会落后的,struts2整合了webwork,最重要的一个特性就是拦截器(拦截器)。在Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个 action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。谈到拦截器,还有一个词大家应该知道——拦截器链(Interceptor Chain,在Struts 2中称为拦截器栈Interceptor Stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。既然如此,我们看看struts2中拦截器调用的时序图:
yeah,和我们之前的动态代理的原理一样,那么我们就使用拦截器来完成我们这样的需求,下篇文章我们将解决这些问题。
当然最简单的实现就是在每个Action的CRUD方法中首先判断登陆用户的权限,如果没有此方法操作的权限就可以抛出一个说明原因的异常,这样在一个庞大的系统中,这样类似权限检查的代码就会在众多Action中蔓延,这将会使得业务逻辑代码变得晦涩,对系统以后的维护变得困难,严重违反了单一职责原则。
相反,我们可以将一个权限控制剥离到一个代理中,这样Action几乎不需要改动就可以达到权限控制的目的。以下是详细实现。
1.基本的实现步骤:使用一个动态代理类(Proxy),通过拦截一个对象(我们实现crud操作的一个抽象类AbstractCRUDAction)的行为("load", "store","remove")并添加我 们需要的功能(权限控制)来完成。Java中的java.lang.reflect.Proxy类和 java.lang.reflect.InvocationHandler接口为我们实现动态代理类提供了一个方案.
2.实现InvocationHandler接口的invoke()方法,所有代理对象的调用都会发送到invoke方法,所以只要在调用前后实现自己的控制即可,这里我们在调用代理方法之前进行权限控制,伪代码如下:
//在调用核心功能之前作一些动作
……
//调用核心功能
m.invoke(obj, args);
//在调用核心功能以后做一些动作
……
……
//调用核心功能
m.invoke(obj, args);
//在调用核心功能以后做一些动作
……
package com.waimai.experiment;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.waimai.web.AbstractCRUDAction;
/**
* 描述:
*
* @author Stone yang 创建日期:2007-5-20
* @version pattern Study 技术支持: <a
* href="http://blog.csdn.net/yq76034150">http://blog.csdn.net/yq76034150</a>
*/
public class ActionProxy implements InvocationHandler {
//需要权限控制的方法列表
private List<String> methods;
private Object target;
/**
* 描述:构造函数
*
* @param methods
* @param target
* @author Stone yang 创建时间:2007-5-20
*/
public ActionProxy(Object target, String... methodNames) {
this.methods = Arrays.asList(methodNames);
this.target = target;
}
public static Object getInstance() {
ActionProxy actionProxy = new ActionProxy(new AbstractCRUDAction(), "load","store","remove");
return Proxy.newProxyInstance(AbstractCRUDAction.class.getClassLoader(),
new Class[] {AbstractCRUDAction.class}, actionProxy);
}
/**
* 描述:
*
* @author Stone yang
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO 自动生成方法存根
this.doBegin();
return method.invoke(target, args);
}
/**
*
* 描述:
*
* @author Stone yang 创建时间:2007-5-20
*/
private void doBegin() {
// TODO 自动生成方法存根
//权限检查代码
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.waimai.web.AbstractCRUDAction;
/**
* 描述:
*
* @author Stone yang 创建日期:2007-5-20
* @version pattern Study 技术支持: <a
* href="http://blog.csdn.net/yq76034150">http://blog.csdn.net/yq76034150</a>
*/
public class ActionProxy implements InvocationHandler {
//需要权限控制的方法列表
private List<String> methods;
private Object target;
/**
* 描述:构造函数
*
* @param methods
* @param target
* @author Stone yang 创建时间:2007-5-20
*/
public ActionProxy(Object target, String... methodNames) {
this.methods = Arrays.asList(methodNames);
this.target = target;
}
public static Object getInstance() {
ActionProxy actionProxy = new ActionProxy(new AbstractCRUDAction(), "load","store","remove");
return Proxy.newProxyInstance(AbstractCRUDAction.class.getClassLoader(),
new Class[] {AbstractCRUDAction.class}, actionProxy);
}
/**
* 描述:
*
* @author Stone yang
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO 自动生成方法存根
this.doBegin();
return method.invoke(target, args);
}
/**
*
* 描述:
*
* @author Stone yang 创建时间:2007-5-20
*/
private void doBegin() {
// TODO 自动生成方法存根
//权限检查代码
}
}
3.调用代理类实现系统功能,代码大致如此:
package com.waimai.experiment;
import com.waimai.web.CaiTypeAction;
/**
* 描述:
* @author Stone yang 创建日期:2007-5-20
* @version pattern Study
* 技术支持: <a href="http://blog.csdn.net/yq76034150">http://blog.csdn.net/yq76034150</a>
*/
public class ProxyInvokeMock {
/**
* 描述:
* @param args
* @author Stone yang
* 创建时间:2007-5-20
*/
public static void main(String[] args) {
// TODO 自动生成方法存根
CaiTypeAction actionProxy = (CaiTypeAction)ActionProxy.getInstance();
//调用需要进行权限控制的方法
actionProxy.load();
// actionProxy.store();
// actionProxy.remove();
}
}
import com.waimai.web.CaiTypeAction;
/**
* 描述:
* @author Stone yang 创建日期:2007-5-20
* @version pattern Study
* 技术支持: <a href="http://blog.csdn.net/yq76034150">http://blog.csdn.net/yq76034150</a>
*/
public class ProxyInvokeMock {
/**
* 描述:
* @param args
* @author Stone yang
* 创建时间:2007-5-20
*/
public static void main(String[] args) {
// TODO 自动生成方法存根
CaiTypeAction actionProxy = (CaiTypeAction)ActionProxy.getInstance();
//调用需要进行权限控制的方法
actionProxy.load();
// actionProxy.store();
// actionProxy.remove();
}
}
最后回到我们需要解决的问题上,由于我们是对struts2的Action进行动态代理,在整个struts的整个框架中,我们并不显式调用 action,这可怎么办呢?呵呵,不用着急,在aop风靡的今天,struts不会落后的,struts2整合了webwork,最重要的一个特性就是拦截器(拦截器)。在Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个 action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。谈到拦截器,还有一个词大家应该知道——拦截器链(Interceptor Chain,在Struts 2中称为拦截器栈Interceptor Stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。既然如此,我们看看struts2中拦截器调用的时序图:
yeah,和我们之前的动态代理的原理一样,那么我们就使用拦截器来完成我们这样的需求,下篇文章我们将解决这些问题。