1. 程式人生 > >JDK反序列化時修改類的全限定性名

JDK反序列化時修改類的全限定性名

應用場景

Spring Security OAuth2有一個奇葩的設計,那就是它將與access_token相關的所有屬於都封裝到OAuth2AccessToken中,然後儲存時會直接將該物件序列化成位元組寫入資料庫。我們在資源伺服器中想要直接讀資料庫來取出access_token來驗證令牌的有效性,然而又不想引入Spring Security的相關依賴汙染jar包。這時可以將Spring Security中OAuth2AccessToken的唯一實現類DefaultOAuth2AccessToken的原始碼copy到我們的專案中,然後通過JDBC讀取byte[], 通過JDK自帶的反序列化機制來還原DefaultOAuth2AccessToken

物件。這時就會遇到問題,即原來的OAuth2AccessToken所在包是以org.springframework.security開頭的,而我們copy過來原始碼後,包名是以我們自己定義的包cn.com.XXXX開頭的,這樣在反序列化時,即使兩個類的欄位完全一樣,但由於位元組流中儲存的類資訊的全限定性名不同,也會導致反序列化失敗。

解決方案

我們可以定義子類繼承JDK的ObjectInputStream,然後重寫readClassDescriptor()方法:

@Override
        protected ObjectStreamClass readClassDescriptor
() throws IOException, ClassNotFoundException { ObjectStreamClass read = super.readClassDescriptor(); if (read.getName().startsWith("原包名")) { Class type = Class.forName(read.getName().replace("新包名")); return ObjectStreamClass.lookup(type); } return
read; }

這樣在反序列化時就不會報錯了。原理並不複雜,其實就是在解析位元組流時,將解析後應為org.springframework.security.oauth2.common.DefautOAuthTokenclass,替換成了我們自己copy過來原始碼的cn.com.XXXXXX.DefaultOAuthToken從而達到”欺騙”的目的。在該場景下,我們就可以做到在資源提供方不引入 Spring Security 框架而只使用Spring Security OAuth2的授權服務。資源提供方直接讀資料庫來驗證令牌的有效性,而不是向授權服務查詢。