读取具有localdate字段的对象时,自定义objectinputstream出现意外行为

sz81bmfz  于 2021-07-13  发布在  Java
关注(0)|答案(1)|浏览(298)

我用的是这个回购的例子https://github.com/kojenov/serial/tree/master/3-4.%20upload,它提供了一种方法,通过定义一个自定义objectinputstream并重写一个受保护的方法resolveclass,在这个方法中,我们必须指定哪些类允许反序列化,从而指定一种在java中防止不安全反序列化的方法。我的问题是,我在planet类中添加了一个localdate字段,当我反序列化序列化对象时,会出现以下异常:
除不支持的类以外的类无效;java.time.ser文件
我在网上搜索,却找不到其他遇到这个问题的人,所以我真的很困惑。我尝试用而不是localdate添加localdatetime,同样的错误再次发生。据我所知,java.time.ser类是该包中类层次结构中某个受保护的类。localdate类是可序列化的,因此不应发生这种情况。我确信问题出在localdate中,因为如果我使该字段按预期工作,那么 transient 代码就会正常工作。我是遗漏了什么还是仅仅是java对象序列化的一个bug?顺便说一句,这些例子最初来自阿列克谢·科耶诺夫(alexei kojenov)的一次演讲,他的网站是kojenov.com,但我找不到他的电子邮件亲自询问他。

e1xvtsh3

e1xvtsh31#

序列化是一种递归过程,这意味着在序列化复杂对象时,首先需要序列化其所有属性。反序列化也会发生同样的情况。 Planet 对象包含类型为的字段 int , double 以及 java.lang.String 它们是原语,不需要特殊的(反)序列化。 LocalDate 或者 LocalDateTime 不是原语,它们被序列化然后反序列化 SafeObjectInputStream .

序列化黑客

正如书中所说 java.io.Serializable 对象可以修改它们的序列化行为,甚至通过定义方法将序列化委托给另一个类 writeReplace .
javadoc引用:
在将对象写入流时,需要指定备用对象的可序列化类应使用精确签名实现此特殊方法: ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException; 如果存在此writereplace方法,并且可以从正在序列化的对象的类中定义的方法访问它,则序列化将调用此writereplace方法。因此,该方法可以具有私有、受保护和包私有访问。对这个方法的子类访问遵循java可访问性规则。
两者 LocalDate 以及 LocalDateTime 利用这种可能性并定义 writeReplace 方法。
举个例子, java.time.LocalDate 的实现:

private Object writeReplace() {
    return new Ser(Ser.LOCAL_DATE_TYPE, this);
}
``` `java.time.Ser` 是一个包私有的final类,用作 `java.time.*` 物体。
因此,当你连载 `java.time.LocalDate` 或者 `java.time.LocalDateTime` ,实际上 `java.time.Ser` 正在序列化。

#### 自定义反序列化程序

之前我们发现 `java.time.LocalDate` 已序列化为 `java.time.Ser` . 现在,让我们尝试用 `SafeObjectInputStream` .
在反序列化之前, `resolveClass` 方法被调用:

@Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!input.getName().equals(Planet.class.getName())) {
throw new InvalidClassException("Unsupported class", input.getName());
}
return super.resolveClass(input);
}

它检查类名是否等于 `Planet.class.getName()` ,但是 `java.time.Ser` 不是,这就是为什么你得到一个例外。

#### 解决方案

要解决此问题,您需要添加 `java.time.Ser` 添加到受信任类的列表。我建议修改你的 `SafeObjectInputStream` 下一步:

public class SafeObjectInputStream extends ObjectInputStream {

private final List supportedClasses = List.of(Planet.class.getName(), "java.time.Ser");

public SafeObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}

@Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!supportedClasses.contains(input.getName())) {
throw new InvalidClassException("Unsupported class ", input.getName());
}
return super.resolveClass(input);
}
}

注: `List.of` 是在Java9中引入的。如果java版本小于9,则可以用 `Arrays.asList` .

相关问题