环境
组件名称 | 版本 |
---|---|
JDK | 17 |
SpringBoot | 3.2.0 |
Gradle | 8.5 |
ProGuard | 7.4.2 |
项目集成ProGuard
参考github上progaurd官方提供的配置,选择了7.4.2版本,但是使用此配置,项目编译直接失败。
报错信息:Could not get unknown property 'ProGuardTask' for project ':proguard-example-config' of type org.gradle.api.Project
于是根据相应的错误提示,修改了相关配置,修改后的配置关键点如下:
kotlin
复制代码
import proguard.gradle.ProGuardTask buildscript { repositories { mavenCentral() } dependencies { classpath("com.guardsquare:proguard-gradle:7.4.2") } } tasks.register('proguard', ProGuardTask) { configuration file('proguard.pro') injars(tasks.named('jar').flatMap { it.archiveFile }) def javaHome = System.getProperty('java.home') if (System.getProperty('java.version').startsWith('1.')) { libraryjars "${javaHome}/lib/rt.jar" } else { libraryjars "${javaHome}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class' } verbose() outjars layout.buildDirectory.file("libs/${project.name}-minified.jar") }
引入之后,重新build
一下,就可以看到proguard task
了,执行该任务,就可以在build/libs
下看到相应的混淆Jar包
混淆配置
好了,看到这,终于到了这篇文章的重点了。
在我们项目中,混淆的代码主要是一些自研中间件的相关配置。在对外项目中,只需要引入这个中间件Jar包即可,不需要做项目中做额外配置。
混淆原则
了解到此需要之后,我们就可以简单总结出几个原则对代码做混淆了。
-
对外使用的类不做混淆
-
Spring相关的类不做混淆
-
重写的方法、get/set方法不做混淆
-
异常、注解、枚举不做混淆
混淆配置文件
混淆的配置文件如下:proguard.pro
ruby
复制代码
# JDK目标版本17 -target 17 # 禁用shrink(删除未使用的类和成员) -dontshrink # 禁用优化(字节码级别的优化) -dontoptimize # 忽略打包时的警告信息 -ignorewarnings # 不跳过非公共类文件和成员 -dontskipnonpubliclibraryclasses -dontskipnonpubliclibraryclassmembers # 类名不使用混合大小写 -dontusemixedcaseclassnames # 优化时允许访问并修改有修饰符的类和类的成员 -allowaccessmodification # 混淆类名之后,对使用Class.forName("className")之类的地方进行相应替代 -adaptclassstrings # 保留所有包名 -keeppackagenames # 使用唯一的类成员名称 -useuniqueclassmembernames # 保留异常、注解等特殊信息,避免影响 Spring Boot 启动 -keepattributes Exceptions,InnerClasses,Signature?Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,SyntheticjEnclosingMethod # 保留所有的set/get方法 -keepclassmembers public class * { void set*(***); *** get*(); } ## 保留外部调用类 -keep class com.zl.exaplme.util.MinIOUtil { *; } # 保留带有 Spring 相关注解的类、字段和方法 -keepclassmembers class * { @org.springframework.beans.factory.annotation.Autowired *; @org.springframework.beans.factory.annotation.Qualifier *; @org.springframework.beans.factory.annotation.Value *; @org.springframework.beans.factory.annotation.Required *; @org.springframework.context.annotation.Bean *; @org.springframework.context.annotation.Primary *; @org.springframework.boot.context.properties.ConfigurationProperties *; @org.springframework.boot.context.properties.EnableConfigurationProperties *; @javax.annotation.PostConstruct *; @javax.annotation.PreDestroy *; } # 保留实现 Serializable 接口的类 -keep class * implements java.io.Serializable { *; } # 保留实现 CommandLineRunner 接口的类 -keep class * implements org.springframework.boot.CommandLineRunner { *; } ## 保留使用 @Configuration 注解的类 #-keep @org.springframework.context.annotation.Configuration class * { *; } # ## 保留使用 @Component 注解的类 #-keep @org.springframework.stereotype.Component class * { *; } # ## 保留使用 @Service 注解的类 #-keep @org.springframework.stereotype.Service class * { *; } # 保留使用 @ConfigurationProperties 注解的类 -keep @org.springframework.boot.context.properties.ConfigurationProperties class * { *; } # 保留使用 @Slf4j 注解的类和生成的 log 字段 -keep @lombok.extern.slf4j.Slf4j class * {*;} -keepclassmembers class * { lombok.extern.slf4j.Slf4j log; } # 保留枚举类的所有成员 -keepclassmembers enum * { *; } # 保留主方法 -keepclasseswithmembers public class * { public static void main(java.lang.String[]); } # 保留源文件和行号信息 -renamesourcefileattribute SourceFile -keepattributes SourceFile,LineNumberTable # 忽略所有的警告信息 -dontwarn * # 混淆字典 -applymapping mapping.map
SpringBoot自动注入与混淆Mapping映射
由于我们混淆的主要是中间件代码,使用了SpringBoot的自动注入机制,3.2版本的spring.factories
变更为新的方式,这里需要额外注意一下。
在这里配置的是类的全路径,但是在混淆之后,类名发生了变化。
为了解决这个问题,以及做到更好的混淆,可以使用自定义的混淆字典,在proguard.pro
同级目录下新增一个mapping.map
文件,在这里定义混淆前后的类名映射。
arduino
复制代码
com.zl.exaplme.config.MinIOAutoConfiguration -> com.zl.exaplme.config.xyz:
如将 MinIOAutoConfiguration
混淆成xyz
。
同时将自动注入的类全路径修改为混淆后的类名:com.zl.exaplme.config.xyz
总结
经过以上步骤,就大致完成了一个SpringBoot Starter的代码混淆。