概念
代理模式是Java当中最常用的设计模式之一。其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。而Java的代理机制分为静态代理和动态代理,这里学习Java自带的jdk动态代理机制。
静态代理示例
静态代理是在编译使用时,定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。我们用一个出租房子作为实例讲解。
- 定义一个接口
public interface Rental {
public void sale();
}
- 定义委托类
public class Entrust implements Rental{
@Override
public void sale() {
System.out.println("出租房子");
}
}
- 定义代理类
public class AgentRental implements Rental{
private Rental target;
public AgentRental(Rental target) {
this.target = target;
}
@Override
public void sale() {
System.out.println("房子出租价位有1k-3k");
target.sale();
}
}
然后我们通过生成委托类实例对象,并将对象传入代理类构造函数中。
public class Test {
// 静态代理
public static void consumer(Rental subject) {
subject.sale();
}
public static void main(String[] args) {
Rental test = new Entrust();
System.out.println("使用代理之前:");
consumer(test);
System.out.println("使用代理之后:");
consumer(new AgentRental(test));
}
}
静态代理的优点:
在不改变委托类Entrust源代码的情况下,通过代理类AgentRental来修改委托类Entrust的功能,从而实现“代理”操作。
静态代理的缺点:
当需要过多的代理类对委托类进行修改的情况,会出现如下情况:
- 当接口类需要增加和删除方法的时候,委托类和代理类都需要更改,所以不易维护。
- 同时需要代理多个类的时候,每个委托类都需要编写一个代理类,会导致代理类繁多,不好管理。
正是因为静态代理存在如上的缺点,所以就有了动态代理机制。
动态代理示例
动态代理介绍
Java动态的代理位于java.lang.reflect包下,一般仅涉及java.lang.reflect.Proxy类与InvocationHandler接口,使用其配合反射实现动态代理的操作。
InvocationHandler接口:负责提供调用代理操作,是由代理对象调用处理器实现的接口,定义了一个invoke方法,每个代理对象都有一个关联的接口。当在代理对象上调用方法时,该方法会被自动转发到InvocationHandler.invoke()方法来进行调用。
Proxy类:负责动态构建代理类,提供4个静态方法:
getInvocationHandler(Object proxy)
:通过指定代理类实例查找与它相关联的调用处理器实例getProxyClass(ClassLoader loader, Class<?>... interfaces)
:通过指定类加载器获取动态代理类对象isProxyClass(Class<?> cl)
:通过传入类判断是否为一个动态代理类newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
:通过类加载器、接口组、调用处理器生成代理类
实现过程
通过上述流程图我们首先需要自定义调用处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicAgentRental implements InvocationHandler {
private Object target;
public DynamicAgentRental(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("房子出租价位有1k-3k");
Object result = method.invoke(target, args);
return result;
}
}
之后我们写测试用例
import java.lang.reflect.Proxy;
public class DynameicTest {
public static void main(String[] args) {
// 创建委托类对象实例
Entrust entrust = new Entrust();
// 获取类加载器
ClassLoader classLoader = entrust.getClass().getClassLoader();
// 获取接口组
Class<?>[] interfaces = entrust.getClass().getInterfaces();
// 获取调用处理器
DynamicAgentRental dynamicAgentRental = new DynamicAgentRental(entrust);
// 生成代理类
Rental newProxyInstance = (Rental)Proxy.newProxyInstance(classLoader, interfaces, dynamicAgentRental);
// 执行代理类方法
newProxyInstance.sale();
}
}
ysoserial示例
在ysoserial工具中,很多的poc和exp都用到了动态代理,这里就来看看其中的CommonsCollections1.java
,抛去前边的Transformer
链,之后使用ysoserial.payloads.util.Gadgets
创建动态代理
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
跟进Gadgets.createMemoitizedProxy
方法:
public static <T> T createMemoitizedProxy ( final Map<String, Object> map, final Class<T> iface, final Class<?>... ifaces ) throws Exception {
return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
}
这里继续跟进createMemoizedInvocationHandler
方法:
public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
public static InvocationHandler createMemoizedInvocationHandler ( final Map<String, Object> map ) throws Exception {
return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
}
这里又调用了Reflections.getFirstCtor
,继续跟进:
public static Constructor<?> getFirstCtor(final String name) throws Exception {
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
setAccessible(ctor);
return ctor;
}
这里传入参数name
的值为sun.reflect.annotation.AnnotationInvocationHandler
,很明显这里通过反射返回了AnnotationInvocationHandler
的构造器
之后在createMemoizedInvocationHandler
方法中通过返回的构造器创建了类对象示例(其也就是个调用处理器)并返回
所以在createMemoitizedProxy
方法中的第一个参数是AnnotationInvocationHandler
调用处理器,然后传入createProxy
方法,跟进:
public static <T> T createProxy ( final InvocationHandler ih, final Class<T> iface, final Class<?>... ifaces ) {
final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
allIfaces[ 0 ] = iface;
if ( ifaces.length > 0 ) {
System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length);
}
return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih));
}
该方法前边的几句话都是在创建接口类数组,其就是实现将iface(Map.class)添加到ifaces(new Class[]{}
是个空数组)的第一位
最后通过Proxy.newProxyInstance
去创建代理类,第一个参数为类加载器,第二个参数为类接口数组,第三个参数就是AnnotationInvocationHandler
调用处理器。之后通过iface.cast
方法进行对象类型转换(iface为Map对象类型)
参考文章
JAVA安全基础(三)-- java动态代理机制 - 先知社区
标签:Java,target,代理,Class,public,InvocationHandler,动态,final From: https://www.cnblogs.com/seizer/p/17055308.html