JDK反序列化時修改類的全限定性名
阿新 • • 發佈:2019-01-31
應用場景
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.DefautOAuthToken
的class
,替換成了我們自己copy過來原始碼的cn.com.XXXXXX.DefaultOAuthToken
從而達到”欺騙”的目的。在該場景下,我們就可以做到在資源提供方不引入 Spring Security 框架而只使用Spring Security OAuth2的授權服務。資源提供方直接讀資料庫來驗證令牌的有效性,而不是向授權服務查詢。