ProGuard混淆后Jackson序列化多态类型丢失type标识信息的问题排查
ProGuard混淆后Jackson序列化多态类型丢失type标识信息的问题排查
问题概述
我在使用ProGuard(Gradle插件)混淆的项目里,用Jackson处理多态类型的序列化/反序列化时踩了个大坑:混淆后的构建中,多态类型的序列化会丢失@JsonTypeInfo指定的type属性,直接导致反序列化彻底乱套——本来应该是CarModel子类的对象,变成了LinkedHashMap,运行时直接抛出ClassCastException。
比如未混淆时的正确序列化结果:
{ "owner" : "Jay Leno", "cars" : [ { "type" : "Corvette", "name" : "Corvette", "year" : 1963 }, { "type" : "Aztek", "name" : "Ugly", "year" : 2003 } ] }
混淆后就变成了这样(type字段直接消失,year还被重命名成了a):
{ "owner" : "Jay Leno", "cars" : [ { "a" : 1963, "name" : "Corvette" }, { "a" : 2003, "name" : "Ugly" } ] }
问题原因分析
ProGuard在混淆过程中,会对类名、字段名进行重命名,同时如果没有正确配置规则,会破坏Jackson序列化/反序列化必需的核心信息:
- 关键注解被移除:
@JsonTypeInfo、@JsonSubTypes这些多态标识注解可能被ProGuard误删,导致Jackson无法识别多态类型的映射规则; - 类元数据丢失:多态子类的类名被混淆后,Jackson无法关联到
type字段对应的具体类; - 字段重命名混乱:实体类的字段(比如
year)被重命名,不仅导致序列化字段名异常,还会让反序列化时无法匹配字段。
解决方案:调整ProGuard规则
要解决这个问题,咱们需要在ProGuard配置中添加针对性规则,保护Jackson多态序列化相关的类、注解和元数据:
1. 核心规则补充
添加以下规则,保留Jackson关键注解、多态类的结构及元数据:
# 保留所有注解信息,Jackson依赖注解识别序列化规则 -keepattributes *Annotation* # 保留多态基类和子类的完整结构、字段与方法 -keep class rizzo.test.CarModel { *; } -keep class rizzo.test.Corvette { *; } -keep class rizzo.test.Aztek { *; } # 保留带有多态注解的类 -keep @com.fasterxml.jackson.annotation.JsonSubTypes class * -keep @com.fasterxml.jackson.annotation.JsonTypeInfo class * # 保留子类的@JsonCreator构造方法,确保Jackson能正确实例化对象 -keepclassmembers class * { @com.fasterxml.jackson.annotation.JsonCreator <init>(...); }
2. 修改Gradle的ProGuard任务配置
把上面的规则整合到你的proguard任务中,修改后的完整任务配置如下:
task proguard(type: proguard.gradle.ProGuardTask) { dependsOn classes verbose printmapping "${buildDir}/proguard-mapping.txt" injars "${buildDir}/classes/java/main" outjars "${buildDir}/classes/obfuscated/" libraryjars "${System.getProperty('java.home')}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class' libraryjars "${System.getProperty('java.home')}/jmods/java.logging.jmod", jarfilter: '!**.jar', filter: '!module-info.class' libraryjars "${System.getProperty('java.home')}/jmods/java.desktop.jmod", jarfilter: '!**.jar', filter: '!module-info.class' libraryjars "${System.getProperty('java.home')}/jmods/java.xml.jmod", jarfilter: '!**.jar', filter: '!module-info.class' libraryjars "${System.getProperty('java.home')}/jmods/java.sql.jmod", jarfilter: '!**.jar', filter: '!module-info.class' libraryjars sourceSets.main.compileClasspath keepdirectories // 新增:保护Jackson多态序列化的核心规则 -keepattributes *Annotation* -keep class rizzo.test.CarModel { *; } -keep class rizzo.test.Corvette { *; } -keep class rizzo.test.Aztek { *; } -keep @com.fasterxml.jackson.annotation.JsonSubTypes class * -keep @com.fasterxml.jackson.annotation.JsonTypeInfo class * -keepclassmembers class * { @com.fasterxml.jackson.annotation.JsonCreator <init>(...); } // 原有规则:保留getter/setter和主类入口 keepclassmembers 'class * { \ ** get*(); \ void set*(***); \ }' keep "public class ${appMainClass} { \ public static void main(java.lang.String[]); \ }" renamesourcefileattribute 'SourceFile' keepattributes 'SourceFile,LineNumberTable' doLast { delete "${buildDir}/classes/java/main" copy { from "${buildDir}/classes/obfuscated/" into "${buildDir}/classes/java/main" } } }
3. 验证效果
修改完规则后,运行./gradlew proguard jar构建混淆后的JAR,再执行java -jar build/libs/ProguardJacksonTest.jar,就能看到序列化结果和未混淆时完全一致,反序列化也不会再抛出ClassCastException了。
通用规则优化(可选)
如果你的项目中有大量多态类,可以用更通用的规则代替逐个类的配置:
# 保留所有带有@JsonTypeInfo注解的类及其子类 -keep @com.fasterxml.jackson.annotation.JsonTypeInfo class * { *; } -keep class * extends @com.fasterxml.jackson.annotation.JsonTypeInfo * { *; }
这样就不用手动添加每个类名,适合类数量较多的项目。
备注:内容来源于stack exchange,提问作者E-Riz
相关产品推荐
相关产品推荐

