程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 關於Java對象序列化您不知道的5件事(2)

關於Java對象序列化您不知道的5件事(2)

編輯:關於JAVA

到現在為止,還沒有看到什麼新鮮的或令人興奮的事情,但是這是一個很好的出發點。我們將使用 Person 來發現您可能不 知道的關於 Java 對象序列化 的 5 件事。

1. 序列化允許重構

序列化允許一定數量的類變種,甚至重構之後也是如此,ObjectInputStream 仍可以很好地將其讀出來。Java Object Serialization 規范可以自動管理的關鍵任務是:

  • 將新字段添加到類中
  • 將字段從 static 改為非 static
  • 將字段從 transient 改為非 transIEnt

取決於所需的向後兼容程度,轉換字段形式(從非 static 轉換為 static 或從非 transient 轉換為 transIEnt)或者刪除字段需要額外的消息傳遞。

重構序列化類

既然已經知道序列化允許重構,我們來看看當把新字段添加到 Person 類中時,會發生什麼事情。

如清單 3 所示,PersonV2 在原先 Person 類的基礎上引入一個表示性別的新字段。

清單 3. 將新字段添加到序列化的 Person 中

  1. enum Gender
  2. {
  3. MALE, FEMALE
  4. }
  5. public class Person
  6. implements Java.io.Serializable
  7. {
  8. public Person(String fn, String ln, int a, Gender g)
  9. {
  10. this.firstName = fn; this.lastName = ln; this.age = a; this.gender = g;
  11. }
  12. public String getFirstName() { return firstName; }
  13. public String getLastName() { return lastName; }
  14. public Gender getGender() { return gender; }
  15. public int getAge() { return age; }
  16. public Person getSpouse() { return spouse; }
  17. public void setFirstName(String value) { firstName = value; }
  18. public void setLastName(String value) { lastName = value; }
  19. public void setGender(Gender value) { gender = value; }
  20. public void setAge(int value) { age = value; }
  21. public void setSpouse(Person value) { spouse = value; }
  22. public String toString()
  23. {
  24. return "[Person: firstName=" + firstName +
  25. " lastName=" + lastName +
  26. " gender=" + gender +
  27. " age=" + age +
  28. " spouse=" + spouse.getFirstName() +
  29. "]";
  30. }
  31. private String firstName;
  32. private String lastName;
  33. private int age;
  34. private Person spouse;
  35. private Gender gender;
  36. }

序列化使用一個 hash,該 hash 是根據給定源文件中幾乎所有東西 — 方法名稱、字段名稱、字段類型、訪問修改方法等 — 計算出來的,序列化將該 hash 值與序列化流中的 hash 值相比較。

為了使 Java 運行時相信兩種類型實際上是一樣的,第二版和隨後版本的 Person 必須與第一版有相同的序列化版本 hash(存儲為 private static final serialVersionUID 字段)。因此,我們需要 serialVersionUID 字段,它是通過對原始(或 V1)版本的 Person 類運行 JDK serialver 命令計算出的。

一旦有了 Person 的 serialVersionUID,不僅可以從原始對象 Person 的序列化數據創建 PersonV2 對象(當出現新字段時,新字段被設為缺省值,最常見的是“null”),還可以反過來做:即從 PersonV2 的數據通過反序列化得到 Person,這毫不奇怪。

2. 序列化並不安全

讓 Java 開發人員詫異並感到不快的是,序列化二進制格式完全編寫在文檔中,並且完全可逆。實際上,只需將二進制序列化流的內容轉儲到控制台,就足以看清類是什麼樣子,以及它包含什麼內容。

這對於安全性有著不良影響。例如,當通過 RMI 進行遠程方法調用時,通過連接發送的對象中的任何 private 字段幾乎都是以明文的方式出現在套接字流中,這顯然容易招致哪怕最簡單的安全問題。

幸運的是,序列化允許 “hook” 序列化過程,並在序列化之前和反序列化之後保護(或模糊化)字段數據。可以通過在 Serializable 對象上提供一個 writeObject 方法來做到這一點。

模糊化序列化數據

假設 Person 類中的敏感數據是 age 字段。畢竟,女士忌談年齡。我們可以在序列化之前模糊化該數據,將數位循環左移一位,然後在反序列化之後復位。(您可以開發更安全的算法,當前這個算法只是作為一個例子。)

為了 “hook” 序列化過程,我們將在 Person 上實現一個 writeObject 方法;為了 “hook” 反序列化過程,我們將在同一個類上實現一個 readObject 方法。重要的是這兩個方法的細節要正確 — 如果訪問修改方法、參數或名稱不同於清單 4 中的內容,那麼代碼將不被察覺地失敗,Person 的 age 將暴露。

清單 4. 模糊化序列化數據

  1. public class Person
  2. implements Java.io.Serializable
  3. {
  4. public Person(String fn, String ln, int a)
  5. {
  6. this.firstName = fn; this.lastName = ln; this.age = a;
  7. }
  8. public String getFirstName() { return firstName; }
  9. public String getLastName() { return lastName; }
  10. public int getAge() { return age; }
  11. public Person getSpouse() { return spouse; }
  12. public void setFirstName(String value) { firstName = value; }
  13. public void setLastName(String value) { lastName = value; }
  14. public void setAge(int value) { age = value; }
  15. public void setSpouse(Person value) { spouse = value; }
  16. private void writeObject(Java.io.ObjectOutputStream stream)
  17. throws Java.io.IOException
  18. {
  19. // "Encrypt"/obscure the sensitive data
  20. age = age << 2;
  21. stream.defaultWriteObject();
  22. }
  23. private void readObject(Java.io.ObjectInputStream stream)
  24. throws Java.io.IOException, ClassNotFoundException
  25. {
  26. stream.defaultReadObject();
  27. // "Decrypt"/de-obscure the sensitive data
  28. age = age << 2;
  29. }
  30. public String toString()
  31. {
  32. return "[Person: firstName=" + firstName +
  33. " lastName=" + lastName +
  34. " age=" + age +
  35. " spouse=" + (spouse!=null ? spouse.getFirstName() : "[null]") +
  36. "]";
  37. }
  38. private String firstName;
  39. private String lastName;
  40. private int age;
  41. private Person spouse;
  42. }

如果需要查看被模糊化的數據,總是可以查看序列化數據流/文件。而且,由於該格式被完全文檔化,即使不能訪問類本身,也仍可以讀取序列化流中的內容。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved