15.8.4 Java9增加的过滤功能

15.8.4 Java9增加的过滤功能

Java9ObjectInputStream增加了setObjectInputFilter()getObjectInputFilter()两个方法:

方法 描述
void setObjectInputFilter(ObjectInputFilter filter) Set the serialization filter for the stream.
ObjectInputFilter getObjectInputFilter() Returns the serialization filter for this stream.

其中setObjectInputFilter方法用于为对象输入流设置过滤器

反序列化时 过滤器ObjectInputFilter的checkInput方法会自动触发

当程序通过ObjectInputStream反序列化对象时,过滤器的checkInput()方法会被自动激发,用于检查序列化数据是否有效

ObjectInputFilter是函数式接口

方法 描述
ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo filterInfo) Check the class, array length, number of object references, depth, stream size, and other available filtering information.

checkInput方法返回值

使用checkInput()方法检查序列化数据时有3种返回值。

checkInput()方法返回值 描述
ObjectInputFilter.Status.REJECTED 拒绝恢复
ObjectInputFilter.Status.ALLOWED 允许恢复
ObjectInputFilter.Status.UNDECIDED 未决定状态,程序继续执行检查

ObjectInputStream将会根据ObjectInputFilter的检查结果来决定是否执行反序列化:

  • 如果checkInput方法返回Status.REJECtED,反序列化将会被阻止;
  • 如果checkInput方法返回Status.ALLOWED,程序将可执行反序列化;

程序 反序列化之前先检查数据

下面程序对前的ReadObject.java程序进行改进,该程序将会在反序列化之前对数据执行检查。

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
import java.io.*;

public class FilterTest {
public static void main(String[] args) {
try (// 创建一个ObjectInputStream输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt")))
{
ois.setObjectInputFilter((info) -> {
System.out.println("===执行数据过滤===");
ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter();
if (serialFilter != null) {
// 首先使用ObjectInputFilter执行默认的检查
ObjectInputFilter.Status status = serialFilter.checkInput(info);
// 如果默认检查的结果不是Status.UNDECIDED
if (status != ObjectInputFilter.Status.UNDECIDED) {
// 直接返回检查结果
return status;
}
}
// 如果要恢复的对象不是1个
if (info.references() != 1) {
// 不允许恢复对象
return ObjectInputFilter.Status.REJECTED;
}
// 如果恢复的不是Person类
if (info.serialClass() != null && info.serialClass() != Person.class) {
// 不允许恢复对象
return ObjectInputFilter.Status.REJECTED;
}
return ObjectInputFilter.Status.UNDECIDED;
});
// 从输入流中读取一个Java对象,并将其强制类型转换为Person类
Person p = (Person) ois.readObject();
System.out.println("名字为:" + p.getName() + "\n年龄为:" + p.getAge());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

上面程序中的粗体字代码为ObjectInputStream设置了ObjectInputFilter过滤器(程序使用Lambda表达式创建过滤器),程序重写了checkInput方法。
重写checkInput方法时先使用默认的ObjectInputFilter执行检查,

  • 如果检查结果不是Status.UNDECIDED,程序直接返回检查结果。
  • 接下来程序通过FilterInfo检验序列化数据,
    • 如果序列化数据中的对象不唯一(数据已被污染),程序拒绝执行反序列化;
    • 如果序列化数据中的对象不是Person对象(数据被污染),程序拒绝执行反序列化。

通过这种检查,程序可以保证反序列化出来的是唯一的Person对象,这样就让反序列化更加安全、健壮