JEP290深入学习

关键类的学习

ObjectInputFilter

serialFilter字段的类型就是ObjectInputFilter,这个接口是一个函数接口(可以对方法进行赋值),其中定义的抽象方法是:

Status checkInput(FilterInfo filterInfo);

接受的参数类型FilterInfo是内部的一个接口,封装了各种过滤的信息:

interface FilterInfo {
        /**
         * The class of an object being deserialized.
         * For arrays, it is the array type.
         * For example, the array class name of a 2 dimensional array of strings is
         * "{@code [[Ljava.lang.String;}".
         * To check the array's element type, iteratively use
         * {@link Class#getComponentType() Class.getComponentType} while the result
         * is an array and then check the class.
         * The {@code serialClass is null} in the case where a new object is not being
         * created and to give the filter a chance to check the depth, number of
         * references to existing objects, and the stream size.
         *
         * @return class of an object being deserialized; may be null
         */
        Class<?> serialClass();

        /**
         * The number of array elements when deserializing an array of the class.
         *
         * @return the non-negative number of array elements when deserializing
         * an array of the class, otherwise -1
         */
        long arrayLength();

        /**
         * The current depth.
         * The depth starts at {@code 1} and increases for each nested object and
         * decrements when each nested object returns.
         *
         * @return the current depth
         */
        long depth();

        /**
         * The current number of object references.
         *
         * @return the non-negative current number of object references
         */
        long references();

        /**
         * The current number of bytes consumed.
         * @implSpec  {@code streamBytes} is implementation specific
         * and may not be directly related to the object in the stream
         * that caused the callback.
         *
         * @return the non-negative current number of bytes consumed
         */
        long streamBytes();
    }

这个接口作为一个函数接口,更多的情况下是作为方法引用存在的,比如在RMI中的:

RegsitryImpl::registryFilter

ObjectInputFilter.Config

static / createFilter

Config类的静态代码块中会初始化其内部字段configuredFilter,这个字段会被赋值给serialFilter

从代码上来看,首先会获取一个叫SERIAL_FILTER_PROPNAME的常量(其值是jdk.serialFilter),如果结果不为null的话就传入createFilter方法:

public static ObjectInputFilter createFilter(String pattern) {
  	Objects.requireNonNull(pattern, "pattern");
  	return Global.createFilter(pattern, true);
}

这里又涉及到了Global (implements) ObjectInputFilter这个类,根据官方注释,Global#createFilter方法的作用就是把字符串解析成一个ObjectInputFilter对象(Returns an ObjectInputFilter from a string of patterns),具体分析见后文的Global部分。

get(set)SerialFilter

从方法名字就可以看出来,它们是serialFilter字段的getter和setter方法。

public static ObjectInputFilter getSerialFilter() {
  	synchronized (serialFilterLock) {
    	return serialFilter;
  	}
}
public static void setSerialFilter(ObjectInputFilter filter) {
    Objects.requireNonNull(filter, "filter");
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new SerializablePermission("serialFilter"));
    }
    synchronized (serialFilterLock) {
        if (serialFilter != null) {
            throw new IllegalStateException("Serial filter can only be set once");
        }
        serialFilter = filter;
    }
}

get(set)ObjectInputFilter

获取(初始化)一个ObjectInputStream对象中的ObjectInputFilter类型的字段(即serialFilter)。

public static ObjectInputFilter getObjectInputFilter(ObjectInputStream inputStream) {
    Objects.requireNonNull(inputStream, "inputStream");
    return sun.misc.SharedSecrets.getJavaOISAccess().getObjectInputFilter(inputStream);
}
public static void setObjectInputFilter(ObjectInputStream inputStream,
                                        ObjectInputFilter filter) {
    Objects.requireNonNull(inputStream, "inputStream");
    sun.misc.SharedSecrets.getJavaOISAccess().setObjectInputFilter(inputStream, filter);
}

ObjectInputFilter.Config.Global

Global类是Config内部的静态类,实现了ObjectInputFilter接口,内部也有chekInput()方法的实现,所以可以把Global类的对象看作是一个过滤器,它可以直接赋值到ObjectInputStream.serialFilter字段上。

checkInput

Global类的内部有一个储存filter的列表:

private final List<Function<Class<?>, Status>> filters;

checkInput()方法中会遍历这个列表来检测待反序列化的类:

public Status checkInput(FilterInfo filterInfo) {   
	...       
    Class<?> clazz = filterInfo.serialClass();
    if (clazz != null) {
        ...
        if (clazz.isPrimitive())  {
            // Primitive types are undecided; let someone else decide
            return Status.UNDECIDED;
        } else {
            // Find any filter that allowed or rejected the class
            final Class<?> cl = clazz;
            // 关键代码,用stream来遍历filter的list
            Optional<Status> status = filters.stream()
                .map(f -> f.apply(cl))
                .filter(p -> p != Status.UNDECIDED)
                .findFirst();
            return status.orElse(Status.UNDECIDED);
        }
    }
    return Status.UNDECIDED;
}

<init>

Global的构造方法会根据传入的"String pattern"来创建不同的filter然后添加到上文中提到了存储filter的列表中(实际上列表中存储的是一个个Lambda表达式),官方称之为 ***"Pattern-Based Filters"***。

简单来说,这个String pattern就是一条一条的规则,规定着哪些类不可以被反序列化,哪些类可以被反序列化,这些规则如下(引自官方文档):

  • If the pattern starts with "!", the class is rejected if the rest of the pattern matches, otherwise it is accepted
  • If the pattern contains "/", the non-empty prefix up to the "/" is the module name. If the module name matches the module name of the class then the remaining pattern is matched with the class name. If there is no "/", the module name is not compared.
  • If the pattern ends with ".**" it matches any class in the package and all subpackages
  • If the pattern ends with ".*" it matches any class in the package
  • If the pattern ends with "*", it matches any class with the pattern as a prefix.
  • If the pattern is equal to the class name, it matches.
  • Otherwise, the status is undecided.

createrFilter

接上文的Global#createrFilter方法,这个方法就是传入一个规则,然后初始化了一个Global对象:

static ObjectInputFilter createFilter(String pattern, boolean checkComponentType) {
    Global filter = new Global(pattern, checkComponentType);
    return filter.isEmpty() ? null : filter;
}

小结

小结一下每个类的主要作用:

  • ObjectInputFilter是filter要实现的接口,同时也是一个函数接口(意味着可以传入一个方法引用),其checkInput()方法规定着反序列化check的具体实现。
  • Config类可以看作是一个“配置类”,规定着filter如何初始化,如何为一个ObjectInputStream设置(获取)serialFilter
  • Global类是JEP290规则的具体实现,它可以把一个个的String pattern转化成相应的filter;同时这个类本身也可以作为filter存在。

所以再回过头来看在Global类的静态代码块中所提到的那个常量jdk.serialFilter,这个常量其实就是一个属性名,Java会获取这个属性名(key)所对应的value,然后以这个value作为"String pattern",来创建一个filter(Global对象)。

A process-wide filter is configured via a system property or a configuration file. The system property, if supplied, supersedes the security property value.
  • *System property jdk.serialFilter
  • *Security property jdk.serialFilter in conf/security/java.properties

两种过滤模式

两种过滤模式:

  • 全局过滤
  • 局部过滤

全局过滤

全局过滤是指,在ObjectInputStream的创建过程中,通过ObjectInputFilter.Config这个内部类来为serialFilter赋值,进而所有ObjectInputStream对象在创建的时候就完成了serialFilter字段的初始化工作。

public ObjectInputStream(InputStream in) throws IOException {
    verifySubclass();
    bin = new BlockDataInputStream(in);
    handles = new HandleTable(10);
    vlist = new ValidationList();
    // 从Config中获取serialFilter的值
    serialFilter = ObjectInputFilter.Config.getSerialFilter();
    enableOverride = false;
    readStreamHeader();
    bin.setBlockDataMode(true);
}
// ObjectInputFilter.Config#gerSerialFilter

public static ObjectInputFilter getSerialFilter() {
    synchronized (serialFilterLock) {
        return serialFilter;
    }
}

局部过滤

局部过滤是指,在ObjectInputStream的创建过程中不初始化serialFilter字段,而是在需要的时候对单个ObjectInputStream对象进行单独设置。

private final void setInternalObjectInputFilter(ObjectInputFilter filter) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new SerializablePermission("serialFilter"));
    }
    // Allow replacement of the process-wide filter if not already set
    if (serialFilter != null &&
        serialFilter != ObjectInputFilter.Config.getSerialFilter()) {
        throw new IllegalStateException("filter can not be set more than once");
    }
    if (totalObjectRefs > 0 && !Caches.SET_FILTER_AFTER_READ) {
        throw new IllegalStateException(
            "filter can not be set after an object has been read");
    }
    this.serialFilter = filter;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值