代码保护
1、混淆
代码混淆会污染代码的标识符,降低软件被反编译后源码的可读性,从而提高破解的成本。
1.1、ProGuard
对 Java 代码作混淆处理。
ProGuard Manual: Home | Guardsquare
不推荐代码混淆来对 Java 代码做保护。
代码混淆会扰乱代码或文件的标识符,这会导致 Spring 工程的包扫描和 Bean 管理失效。
proguard.cfg 配置
支持对单个或多个目录、文件、类、方法、变量的标识保持不变。
# 取消 warming
-dontwarn
# 保持目录结构,解决 @ComponentScan 失效
-keepdirectories
# 默是开启的,这里关闭 shrink,即不删除没有使用的类/成员,否则会把 Controller 啥的给删掉(取消 ProGuard 对代码的收缩)
-dontshrink
# 默认是开启的,这里关闭字节码级别的优化,否则 aop 会报错(取消 ProGuard 的优化)
-dontoptimize
# 混淆类名之后,对使用 Class.forName('className') 之类的地方进行相应替代
-adaptclassstrings
# 不混淆所有特殊的类,否则抛出去的异常会出问题
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# 保持 VerifyImpl 类下 verify 和 verifyStatus 两个方法不被混淆
-keep public class com.xxx.yyy.zzz.kkk.VerifyImpl {
public boolean verify();
public com.xxx.yyy.zzz.entity.Id verifyStatus();
}
# 保留 Id 实体类,防止 json 序列和反序列化失败
-keep public class com.xxx.yyy.zzz.entity.Id {*;}
# 枚举类不变
-keep public enum com.xxx.yyy.zzz.constants.WayEnum {*;}
# 保留配置变量
-keep public class com.xxx.yyy.zzz.common.properties.MyProperties {*;}
# 保留指定成员变量
-keepclassmembernames public class com.xxx.yyy.zzz.common.properties.SecurityProperties {
int timeout;
}
# 保留资源文件
-adaptresourcefilecontents META-INF/maven/**
# 保留类名,防止混淆后,bean 名称冲突的问题
-keepnames class com.xxx.yyy.zzz.common.properties.SecurityProperties
-keepnames class com.xxx.yyy.zzz.core.id.**
使用
可以采用 ProGuard 界面或命令行的方式完成代码混淆,也可以使用 ProGuard 官方推荐的第三方 Maven 插件来完成,效果都是差不多。
插件的使用
<build>
<plugins>
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<version>2.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<!--输入的 jar-->
<injar>${project.build.finalName}.jar</injar>
<!--输出的 jar-->
<outjar>${project.build.finalName}.jar</outjar>
<!-- 是否混淆 -->
<obfuscate>true</obfuscate>
<!-- 混淆配置文件 -->
<proguardInclude>${project.basedir}/proguard.cfg</proguardInclude>
<!-- 依赖的 JAVA library -->
<libs>
<lib>${java.home}/lib/rt.jar</lib>
</libs>
</configuration>
</plugin>
<!--springboot 打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.14</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<!--声明 mainClass-->
<mainClass>com.xxx.yyy.zzz.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
如果工程用 SpringBoot 的插件打包,那么需要在 SpringBoot 插件打包前,先进行代码混淆然后再交给 SpringBoot 的插件打包。
1.2、其他
代码混淆和加密 – 开源软件 – OSCHINA – 中文开源技术交流社区
2、加密
对代码编译后的字节码作加密,在加载时再解密,那加密的内容就无法反编译。
2.1、Classfinal
免费,开源。Classfinal 提供了两种方式,一种是基于命令行完成,另一宗基于 Maven 插件完成。ClassFinal: Java字节码加密工具
配置
<plugin>
<!-- https://gitee.com/roseboy/classfinal -->
<groupId>net.roseboy</groupId>
<artifactId>classfinal-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<!--加密打包之后 pom.xml 会被删除,不用担心在 jar 包里找到此密码-->
<password>#</password>
<!--加密的包路径,多个用逗号分隔-->
<packages>com.xxx.server,com.xxx.security</packages>
<!--需要加密的配置文件,多个用逗号分隔-->
<cfgfiles>application.yml</cfgfiles>
<!--不需要加密的类名,多个用逗号分隔-->
<excludes>org.spring</excludes>
<!--jar 包 lib 下需要加密的 jar 包文件名,多个用逗号分隔-->
<libjars>xxx-yyy-core-1.0.0.jar</libjars>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>classFinal</goal>
</goals>
</execution>
</executions>
</plugin>
如果项目工程使用 SpringBoot 插件打包,那么 Classfinal 一定要在 SpringBoot 插件打包后再对 Jar 包加密。
加密后的 Jar 包需要通过以下命令参数来启动
java -javaagent:
-jar
效果
参考