java安全-动态代理
一、静态代理
本质就是:代理类和真实类实现同一个接口,代理里“包一层”真实对象。
1.业务接口
1 2 3
| public interface PayService { void pay(int amount); }
|
2.真实对象
1 2 3 4 5 6
| public class PayServiceImpl implements PayService { @Override public void pay(int amount) { System.out.println("pay " + amount); } }
|
3.代理对象(手写)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class PayServiceProxy implements PayService { private final PayService target;
public PayServiceProxy(PayService target) { this.target = target; }
@Override public void pay(int amount) { long t0 = System.nanoTime(); System.out.println("[LOG] before pay"); target.pay(amount); System.out.println("[LOG] after pay, cost=" + (System.nanoTime() - t0)); } }
|
4.使用
1 2 3 4 5 6
| public class Demo { public static void main(String[] args) { PayService proxy = new PayServiceProxy(new PayServiceImpl()); proxy.pay(100); } }
|
最终实现

pay方法,前后都实现了功能增强
缺点:
每个接口都要手写一个 Proxy 类,太累
想要在同一个某一个接口中的所有方法进行处理同样的事情,静态代理类中的每一个方法都需要重写。
二、动态代理
静态代理的问题:每个接口都得写一个 XXXProxy 类,太麻烦
动态代理就是:只需要写一个 Proxy 类,JDK 运行时直接生成。
1.InvocationHandler类
所有方法都会进这里(相当于“通用代理逻辑”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class TimingLogHandler implements InvocationHandler {
private final Object target;
public TimingLogHandler(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long t0 = System.nanoTime(); System.out.println("【动态代理】before " + method.getName());
Object result = method.invoke(target, args);
System.out.println("【动态代理】after " + method.getName() + ", cost=" + (System.nanoTime() - t0));
return result; } }
|
2.创建代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.lang.reflect.Proxy;
public class Demo2 { public static void main(String[] args) { PayService target = new PayServiceImpl();
PayService proxy = (PayService) Proxy.newProxyInstance( target.getClass().getClassLoader(), new Class[]{PayService.class}, new TimingLogHandler(target) );
proxy.pay(200); } }
|
3.创建代理类参数详解
创建代理对象的核心方法:
1 2 3 4 5
| Object proxy = Proxy.newProxyInstance( 类加载器, 接口数组, 调用处理器 );
|
参数一:ClassLoader(类加载器)
1
| target.getClass().getClassLoader()
|
- 用来加载生成的代理类
- 决定代理类“由谁加载到 JVM 中”
参数二:interfaces(代理要实现的接口)
1
| target.getClass().getInterfaces()
|
- 指定代理类要实现哪些接口
- JDK 动态代理 只能代理接口
参数三:InvocationHandler(调用处理器 / 拦截器)
1
| new TimingLogHandler(target)
|
内部执行入口
1
| public Object invoke(Object proxy, Method method, Object[] args)
|
三、在反序列化中的利用
简单演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| public class ProxyDeserializeDemo {
public static class B implements java.io.Serializable { private static final long serialVersionUID = 1L;
public void f(String from) { System.out.println(">>> B.f() 被调用!触发来源 = " + from); } }
public static class H implements InvocationHandler, Serializable { private final B target;
public H(B target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName();
if ("hashCode".equals(name)) { target.f("hashCode() during HashMap.readObject"); return 123; } return method.invoke(this, args); } }
public static void main(String[] args) throws Exception { B b = new B(); O proxy = (O) Proxy.newProxyInstance( O.class.getClassLoader(), new Class[]{O.class}, new H(b) );
Map<Object, String> map = new HashMap<>(); map.put(proxy, "value");
byte[] data; try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) { oos.writeObject(map); data = bos.toByteArray(); }
System.out.println("=== 开始反序列化 ===");
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) { Object obj = ois.readObject(); }
System.out.println("=== 反序列化结束 ==="); } }
|
1 2 3 4
| A[O] -> O.abc //正常方法 O[O2] invoke -> O2.f // 此时将 B 去替换 O2 最后 ----> O[B] invoke -> B.f // 达到漏洞利用效果
|
示例中,使用porxy的hashcode方法代替掉 abc 无害方法,可以调试尝试食用!!!
代理的价值在反序列化链里不是“它会被反序列化自动执行”,而是:
它能把一次“看似无害的方法调用”(hashCode/toString/compare/abc)劫持成你 invoke() 里想做的任何事。