Java反序列化基础3——JDK动态代理

Java反序列化基础3——JDK动态代理

1755237676939-a98bc306-ebb7-4199-ae12-be2c32e71b50.png

代理模式的通用类图

上图中,Subject是一个抽象类或者接口,RealSubject是实现方法类,具体的业务执行,Proxy则是RealSubject的代理,直接和client接触的。

静态代理

以租房为例,首先定义一个租房的接口

public interface IRentHouse{
    void rentHouse();
}

定义租房的实现类

public class RentHouse implements IRentHouse {
    @Override
    public void rentHouse() {
        System.out.println("租了一间房子。。。");
    }
}

再定义一个中介,****在中介中传入要租的房子,中介帮你完成租房操作,这就是代理

在中介中也实现了租房的接口

public class IntermediaryProxy implements IRentHouse {

    private IRentHouse rentHouse;

    public IntermediaryProxy(IRentHouse irentHouse){
        rentHouse = irentHouse;
    }

    @Override
    public void rentHouse() {
        System.out.println("交中介费");
        rentHouse.rentHouse();
        System.out.println("中介负责维修管理");
    }
}

在main方法中进行测试

public class Main {

    public static void main(String[] args){
        //定义租房
        IRentHouse rentHouse = new RentHouse();
        //定义中介
        IRentHouse intermediary = new IntermediaryProxy(rentHouse);
        //中介租房
        intermediary.rentHouse();
    }
}

返回信息

交中介费
租了一间房子。。。
中介负责维修管理

总的来说就是一个接口,一个实体实现类,一个代理实现类,一个启动器

静态代理的优缺点

**优点:**职责清晰,扩展性好,保护真实主题

**缺点:**代码冗余,维修困难,灵活性差

动态代理

所有的interface类型的变量总是通过某个实例向上转型并赋值给接口类型变量的:

CharSequence cs = new StringBuilder();

那有没有可能不编写实现类,直接在运行期间创建某个interface 的实例呢?

这是可能的,java标准库提供了一种动态代理机制,可以在运行期间动态创建某个interface的实例。

一个简单的动态代理实现如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

//返回结果
// public abstract void org.example.Hello.morning(java.lang.String)
// Good morning, Bob


  • 定义了一个InvocationHandler实例,它负责实现接口的方法调用
    • Object invoke(Object proxy, 方法 method, Object[] args);
    • proxy** – 调用该方法的代理实例**
    • method-被调用的方法对应的 Method 对象
    • Object[] args-方法调用时传入的参数数组
  • 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    • 使用的ClassLoader,通常就是接口类的ClassLoader;
    • 需要实现的接口数组,至少需要传入一个接口进去;
    • 用来处理接口方法调用的InvocationHandler实例。
  • 将返回的Object强制转型为接口

所以如果用动态代理表示租房过程就是

接口类:

public interface IRentHouse {
    public void add();
    public void delete();
    public void update();
    public void query();

}

实体实现类



public class RentHouse implements IRentHouse {
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}

代理实现类


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class IntermediaryProxy implements InvocationHandler {

    IRentHouse rentHouse;
    
    public void setIntermediaryProxy(IRentHouse iRentHouse) {
        this.rentHouse = iRentHouse;
    }

    // 动态生成代理类实例
    public Object getProxy(){
        Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), rentHouse.getClass().getInterfaces(), this);
        return obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method);
        System.out.println("调用了"+method.getName());
        Object obj=method.invoke(rentHouse,args);
        return obj;
    }
    //业务自定义需求
    public void log(Method method){
        System.out.println("[Info] " + method.getName() + "方法被调用");
    }
}

启动器:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Renttext {

        public static void main(String[] args){

            RentHouse rentHouse = new RentHouse();

            IntermediaryProxy intermediaryProxy=new IntermediaryProxy();
            intermediaryProxy.setIntermediaryProxy((IRentHouse) rentHouse);

            IRentHouse proxy=(IRentHouse) intermediaryProxy.getProxy();

            proxy.add();
            proxy.delete();
            proxy.query();
            proxy.update();

        }
}

运行结果:

1755256599540-f6d642d3-3156-47f5-8340-f6d9e03efc68.png

在反序列化中动态代理的作用

假设存在一个能漏洞利用的类B.f,比如Runtime.exec

我们将入口类定义为A,最理想的情况是 A[O] -> O.f,那么我们把B传入A就行,但是A中存在O.abc方法,如果O是一个动态代理的话,O里的invoke方法中存在.f的方法,便可以漏洞利用。

相当于

readObject->反序列化自动执行

invoke->有函数调用

更新: 2025-08-16 10:08:51
原文: https://www.yuque.com/cindahy/ukztx0/nrbhiyngnl4c80p3

评论