目录
一、什么是CGLIB
在学习Spring AOP 的时候我们知道Spring AOP 的实现方式是通过JDK 的动态代理和CGLIB的动态代理实现的,那么什么是CGLIB呢?
CGLIB其实就是封装了ASM(Java字节码操控框架)了的功能强大,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
二、CGLIB增强
我们常见的就是Enhancer类增强方式。它可以动态生成一个子类使方法可以被拦截;处理实现接口,该类还额可以动态生成父类中非final方法的子类,并提供回调用户自定义拦截器的钩子。原生的和普遍被使用的回调类型是MethodInterceptor ,它在AOP方面实现了环绕通知,也就是说你既可以在调用父类方法之前和之后调用自定义的增强代码。此外,你可以在调用父类方法前修改入参,甚至是根本就不调用父类方法。 虽然,MethodInterceptor 是通用的,足以满足任何拦截需求,但是它往往矫枉过正。为了简单性和性能增加了专门的回调类型 如LazyLoader,经常一个回调在每个拦截器中都没调用,你可以通过CallbackFilter 控制在每个方法上使用哪个回调。有这样一个Service 提供了数据的增删改查功能,使用自定义注解标注每个方法被访问时要求的权限
2.1 代码示例
假设有这样一个服务,我们需要针对每个方法进行权限校验,只有有相应权限(ADD,UPDATE,QUERY,DELETE)的用户才可以执行对应的方法。那么如何限制到方法维度呢?为了提升通用性,我们可以使用基于注解的方式解决。
2.1.1 权限注解
/**
*
* @author zhangwei_david
* @version $Id: permission.java, v 0.1 2015年1月19日 下午8:58:56 zhangwei_david Exp $
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
String[] value();
}
2.1.2 权限校验
我们现在已经可以在方法上加上注解了那么如何进行方法维度的权限校验呢?我们看看如下代码,
如果方法上有权限控制的注解,我们就判断当前用户的角色是否符合预期。
/**
*权限工具类
* @author zhangwei_david
* @version $Id: AuthUtils.java, v 0.1 2015年1月19日 下午9:14:07 zhangwei_david Exp $
*/
public class AuthUtils {
/**
* 判断当前用户角色是否具有当前方法的访问权限
*
* @param method
* @param role
* @return
*/
public static boolean isGrantPermission(Method method, String role) {
String[] roles = null == method.getAnnotation(Permission.class) ? null : method
.getAnnotation(Permission.class).value();
if (roles == null || roles.length == 0) {
return true;
}
for (String string : roles) {
if (string.equals(role)) {
return true;
}
}
return false;
}
}
2.1.3 数赋服务类
在有了注解以及权限判断工具以后,我们来看看这个数据服务类,提供增删改查,同时针对不同的方法进行权限控制,代码如下:
/**
*数据服务类
* @author zhangwei_david
* @version $Id: TableDAO.java, v 0.1 2015年1月19日 下午8:16:14 zhangwei_david Exp $
*/
public class MyService {
@Permission("ADD")
public void add() {
System.out.println("create() is running !");
}
@Permission({ "ADD", "QUERY", "UPDATE", "QUERY" })
public void query() {
System.out.println("query() is running !");
}
@Permission({ "UPDATE" })
public void update() {
System.out.println("update() is running !");
}
@Permission("DELETE")
public void delete() {
System.out.println("delete() is running !");
}
}
2.1.4权限校验代理
我们已经有基于注解的权限控制服务类,也有权限校验的辅助工具,那么什么时候执行权限校验呢?直接调用MyService似乎是无法进行权限控制的。下面就需要CGLIB出场了
首先定义一个MethodInterceptor来做方法拦截处理
/**
*安全代理类
* @author zhangwei_david
* @version $Id: AuthProxy.java, v 0.1 2015年1月19日 下午8:20:36 zhangwei_david Exp $
*/
public class AuthProxy implements MethodInterceptor {
private String role;
/**
* @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
*/
public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3)
throws Throwable {
if (AuthUtils.isGrantPermission(method, role)) {
return arg3.invokeSuper(arg0, arg2);
}
System.out.println("expected role is "
+ Arrays.toString(method.getAnnotation(Permission.class).value())
+ " but now is " + role);
return null;
}
public AuthProxy(String role) {
super();
this.role = role;
}
}
在MethodInterceptor基础上使用Enhancer()Myservice 进行增强。
/**
*
* @author zhangwei_david
* @version $Id: TableDAOFactory.java, v 0.1 2015年1月19日 下午8:16:47 zhangwei_david Exp $
*/
public class DAOFactory {
public static MyService getInstance(AuthProxy authProxy) {
Enhancer eh = new Enhancer();
eh.setSuperclass(MyService.class);
eh.setCallback(authProxy);
return (MyService) eh.create();
}
}
2.1.5测试代码
/**
*
* @author zhangwei_david
* @version $Id: Client.java, v 0.1 2015年1月19日 下午8:18:18 zhangwei_david Exp $
*/
public class Client {
/**
*
* @param args
*/
public static void main(String[] args) {
MyService tableDAO = DAOFactory.getInstance(new AuthProxy("ADD"));
tableDAO.add();
tableDAO.delete();
tableDAO.update();
tableDAO.query();
}
}
结果是:
create() is running !
expected role is [DELETE] but now is ADD
expected role is [UPDATE] but now is ADD
query() is running !
通过上述结果,我们可以发现现在是符合我们预期的。那有没有什么不足呢?
发现在AuthUtils判断了一个类是否有权限控制的注解,我们可以想一下如果一个类都没有权限控制为什么还需要进入权限的拦截器中呢?
2.1.6 多过滤器优化
/**
*
* @author zhangwei_david
* @version $Id: TableDAOFactory.java, v 0.1 2015年1月19日 下午8:16:47 zhangwei_david Exp $
*/
public class DAOFactory {
public static MyService getInstance(AuthProxy authProxy) {
Enhancer eh = new Enhancer();
eh.setSuperclass(MyService.class);
eh.setCallbacks(new Callback[] { authProxy, NoOp.INSTANCE });
eh.setCallbackFilter(new PermissionControlleFilter());
return (MyService) eh.create();
}
}
/**
*
* @author zhangwei_david
* @version $Id: PermissionControlleFilter.java, v 0.1 2015年1月19日 下午10:06:09 zhangwei_david Exp $
*/
public class PermissionControlleFilter implements CallbackFilter {
/**
* @see net.sf.cglib.proxy.CallbackFilter#accept(java.lang.reflect.Method)
*/
public int accept(Method method) {
if (method.getAnnotation(Permission.class) != null) {
return 0;
}
return 1;
}
}
这样就是表示如果一个方法中没有使用权限注解标注改方法的权限的化就使用第二个拦截器,第二个拦截器
NoOp.INSTANCE
是CGLIB预定义的一个空个回调。