编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Java中为什么不推荐使用 JDK 自带的序列化?

wxchong 2024-06-08 22:56:31 开源技术 42 ℃ 0 评论

在Java开发中,序列化和反序列化是非常常见的操作,主要用于对象的持久化和网络传输。然而,JDK 自带的序列化方式(java.io.Serializable)却不推荐直接使用。下面我们从以下几个方面详细解析:


  1. 不支持跨语言调用
  2. 性能差
  3. 存在安全问题


1. 不支持跨语言调用


JDK 自带的序列化方式仅适用于Java语言。如果我们的系统需要与其他编程语言(如C++、Python、Go等)进行交互,JDK 自带的序列化方式显然不能满足需求。为了支持跨语言调用,我们通常会选择其他序列化框架,如JSON、Protobuf、Thrift等。


示例:Protobuf


Protobuf(Protocol Buffers)是Google开发的一种语言无关、平台无关的可扩展机制,用于序列化结构化数据。下面是一个简单的示例:


protobuf


// person.proto
syntax = "proto3";

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
}



Java代码生成并使用Protobuf对象:


java


import com.example.PersonProto.Person;

public class ProtobufExample {
    public static void main(String[] args) throws Exception {
        // 序列化
        Person person = Person.newBuilder().setName("John").setId(123).setEmail("john@example.com").build();
        byte[] data = person.toByteArray();
        
        // 反序列化
        Person deserializedPerson = Person.parseFrom(data);
        System.out.println(deserializedPerson);
    }
}



这种方式不仅可以在Java中使用,还可以在其他语言中使用相同的.proto文件生成对应的代码,进行跨语言调用。


2. 性能差


JDK 自带的序列化性能较低,主要原因在于序列化后的字节数组体积较大,导致传输成本加大。下面是一个简单的性能对比实验,使用JDK自带的序列化和Protobuf来序列化相同的数据。


示例:性能对比


java


import java.io.*;

public class SerializationPerformanceTest {

    static class Person implements Serializable {
        private String name;
        private int id;
        private String email;

        public Person(String name, int id, String email) {
            this.name = name;
            this.id = id;
            this.email = email;
        }
    }

    public static void main(String[] args) throws Exception {
        Person person = new Person("John", 123, "john@example.com");

        // JDK 序列化
        long startTime = System.nanoTime();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(person);
        byte[] jdkData = baos.toByteArray();
        long jdkDuration = System.nanoTime() - startTime;
        System.out.println("JDK Serialization time: " + jdkDuration + " ns");

        // Protobuf 序列化
        startTime = System.nanoTime();
        PersonProto.Person protoPerson = PersonProto.Person.newBuilder().setName("John").setId(123).setEmail("john@example.com").build();
        byte[] protoData = protoPerson.toByteArray();
        long protoDuration = System.nanoTime() - startTime;
        System.out.println("Protobuf Serialization time: " + protoDuration + " ns");

        // 比较字节数组大小
        System.out.println("JDK Serialized size: " + jdkData.length + " bytes");
        System.out.println("Protobuf Serialized size: " + protoData.length + " bytes");
    }
}



通常情况下,Protobuf的序列化时间更短,序列化后的数据也更小。


3. 存在安全问题


JDK 自带的序列化和反序列化机制存在安全隐患。攻击者可以通过构造恶意输入数据,利用反序列化过程中的漏洞,执行任意代码。为了更好地理解这个问题,我们可以看一个简单的示例。


示例:反序列化漏洞


java


import java.io.*;

class Exploit implements Serializable {
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        System.out.println("Exploit code executed!");
    }
}

public class DeserializationVulnerabilityTest {
    public static void main(String[] args) throws Exception {
        // 序列化恶意对象
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(new Exploit());
        byte[] exploitData = baos.toByteArray();

        // 反序列化恶意对象
        ByteArrayInputStream bais = new ByteArrayInputStream(exploitData);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();  // 这里会执行 Exploit 类中的 readObject 方法
    }
}



在实际应用中,反序列化漏洞可能会导致严重的安全问题,如远程代码执行(RCE)。为了避免这些问题,我们可以使用更安全的序列化框架,如Jackson、Gson等,或者通过自定义的反序列化逻辑进行严格的校验。


总结


通过以上分析,我们可以看到,JDK 自带的序列化方式在跨语言支持、性能和安全性方面都存在明显的不足。因此,在实际开发中,我们更倾向于使用其他更高效、更安全的序列化框架来替代JDK自带的序列化方式。

?

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表