java项目一般是打包成jar运行或者作为依赖给第三方使用的,有的时候,可能不想被别人反编译,于是就对关键部分进行混淆,让代码变得普通人看不懂的地步,很多关键字和变量都是用单个字母表示,达到一种类似加密的效果。
java中混淆框架proguard,准确来说是一个插件,不需要对他进行编码,只需要进行配置即可。代码混淆,并不是把所有代码进行混淆,这样反而会出错,比如枚举类型如果也进行混淆,那么在使用反射创建实例,并给实例赋值的时候,枚举类型会反序列化失败。
proguard提供了可以对哪些包,类,方法等不进行混淆的配置,我们可以将枚举配置在这里。
因为springboot提供了打包插件spring-boot-maven-plugin,所以我们的混淆需要在springboot打包的插件之前,就是我们要对混淆之后的代码通过spring-boot-maven-plugin进行打包。
下面是一个配置示例proguard.cfg:
# 指定不警告尚未解决的引用和其他问题
-dontwarn
# JDK目标版本1.8
-target 1.8
# 不做收缩(删除注释、未被引用代码)
-dontshrink
# 不做优化(变更代码实现逻辑)
-dontoptimize
# 混淆时不使用大小写混合,混淆后的类名为小写
-dontusemixedcaseclassnames
# 不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
# 指定不跳过包可见的库类成员(字段和方法)。
# 默认情况下,proguard在解析库类时会跳过包可见的库类成员。当我们确实引用了包可见的类成员时,需要设置此项
-dontskipnonpubliclibraryclassmembers
# 确定统一的混淆类的成员名称来增加混淆
-useuniqueclassmembernames
# 优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
# 不混淆所有包名
#-keeppackagenames
# 需要保持的属性:异常,注解等
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
# spring 相关的注解,不要混淆
-keepclassmembers class * {
@org.springframework.** *;
@javax.annotation.PostConstruct *;
@javax.annotation.PreDestroy *;
}
# spring 相关的注解,不要混淆
-keepclassmembers class * {
@org.springframework.beans.factory.annotation.Autowired <fields>;
@org.springframework.beans.factory.annotation.Autowired <methods>;
}
#混淆时是否记录日志
#-verbose
# 不混淆所有的set/get方法
-keepclassmembers public class * {void set*(***);*** get*();}
# 不混淆本工程中的部分特殊类
-keep class com.xxx.hello.Hello {*;}
-keep class com.xxx.hello.domain.BookStatus {*;}
# 不混淆所有包含Component等注解的类
-keep @org.springframework.stereotype.Component class * {*;}
-keep @org.springframework.stereotype.Service class * {*;}
-keep @org.springframework.web.bind.annotation.RestController class * {*;}
-keep @org.springframework.context.annotation.Configuration class * {*;}
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.8</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<version>2.0.14</version>
<executions>
<execution>
<!--混淆时刻,这里是打包的时候混淆-->
<phase>package</phase>
<!--使用插件的混淆功能-->
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<proguardVersion>6.1.0</proguardVersion>
<!--是否混淆-->
<obfuscate>true</obfuscate>
<!-- 加载配置文件 -->
<proguardInclude>proguard.cfg</proguardInclude>
<!-- 对什么东西进行加载-->
<injar>${project.build.finalName}.jar</injar>
<!--class 混淆后输出的jar包 或 文件夹-->
<outjar>${project.build.finalName}.jar</outjar>
<!-- 输出目录-->
<outputDirectory>${project.build.directory}</outputDirectory>
<libs>
<!-- Include main JAVA library required.-->
<lib>${java.home}/lib/rt.jar</lib>
<!-- Include crypto JAVA library if necessary.-->
<lib>${java.home}/lib/jce.jar</lib>
</libs>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>6.1.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.xxx.hello.Hello</mainClass>
<classifier>exec</classifier>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
java项目在最后给出来,大致意思是,启动springboot web项目,提供两个接口add,get,一个是用来添加一个book对象,一个用来读取这个book对象。
打包之后的文件:
可以看到hello-1.0-SNAPSHOT_proguard_base.jar要早于另外两个jar,因为它要先混淆。
我们可以通过反编译软件jd-gui查看打包后的jar文件:
这里面对spring相关的都进行过过滤,所以剩下的都是和spring无关的类, 除了BookStatus这个枚举类型之外,都进行了混淆,类名称都直接缩写成了小写字母a,b,c....
测试写入:
测试读取:
都是没问题的,如果我们把BookStatus枚举也进行混淆,那么进行写入的时候,报错:
意思是反序列化失败, 因为没有BookStatus的枚举常量。