Java动态代理机制

概念

代理模式是Java当中最常用的设计模式之一。其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。而Java的代理机制分为静态代理和动态代理,这里学习Java自带的jdk动态代理机制。

静态代理示例

静态代理是在编译使用时,定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。我们用一个出租房子作为实例讲解。

  1. 定义一个接口
public interface Rental {
    public void sale();
}
  1. 定义委托类
public class Entrust implements Rental{
    @Override
    public void sale() {
        System.out.println("出租房子");
    }
}
  1. 定义代理类
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的功能,从而实现“代理”操作。

静态代理的缺点:

当需要过多的代理类对委托类进行修改的情况,会出现如下情况:

  1. 当接口类需要增加和删除方法的时候,委托类和代理类都需要更改,所以不易维护。
  2. 同时需要代理多个类的时候,每个委托类都需要编写一个代理类,会导致代理类繁多,不好管理。

正是因为静态代理存在如上的缺点,所以就有了动态代理机制。

动态代理示例

动态代理介绍

Java动态的代理位于java.lang.reflect包下,一般仅涉及java.lang.reflect.Proxy类与InvocationHandler接口,使用其配合反射实现动态代理的操作。

InvocationHandler接口:负责提供调用代理操作,是由代理对象调用处理器实现的接口,定义了一个invoke方法,每个代理对象都有一个关联的接口。当在代理对象上调用方法时,该方法会被自动转发到InvocationHandler.invoke()方法来进行调用。

Proxy类:负责动态构建代理类,提供4个静态方法:

  1. getInvocationHandler(Object proxy):通过指定代理类实例查找与它相关联的调用处理器实例
  2. getProxyClass(ClassLoader loader, Class<?>... interfaces):通过指定类加载器获取动态代理类对象
  3. isProxyClass(Class<?> cl):通过传入类判断是否为一个动态代理类
  4. 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动态代理机制 - 先知社区

posted @ 2023-01-16 14:20  seizer-zyx  阅读(135)  评论(0编辑  收藏  举报