java代码混淆工具ProGuard混淆插件:https://blog.csdn.net/yinjl123/article/details/138922335
ProGuard Maven Plugin 使用指南:https://blog.csdn.net/gitblog_00427/article/details/141350545
ProGuard Maven Plugin 踩坑记
ProGuard代码混淆器如何使用:https://blog.csdn.net/m0_54892309/article/details/131653721
以下实验是基于:springboot+aop
零、基础概念
在Spring Boot项目中,使用Maven混淆插件(如proguard-maven-plugin
)进行打包是一种提高Java应用安全性、减少代码体积和隐藏实现细节的手段。混淆(Obfuscation)通常涉及重命名类、方法、字段等,移除未使用的代码,以及通过其他方式使反编译和理解原始代码变得更加困难。下面是对混淆插件的作用、优缺点以及可能出现的问题的详细说明。
混淆插件的作用
- 增强安全性:通过使代码难以理解,增加逆向工程的难度,保护源代码不被轻易获取或分析。
- 减小体积:通过移除未使用的代码和类,以及压缩常量字符串等方式,减小最终打包文件的体积。
- 保护知识产权:防止竞争对手通过分析软件来复制或模仿功能。
优缺点
优点
- 提高安全性:如上所述,混淆增加了逆向工程的难度。
- 减小包大小:优化后的代码包更小,便于分发和部署。
- 加速加载时间:更小的包和更紧凑的代码结构可能有助于加快应用的启动和加载时间。
缺点
- 调试困难:混淆后的代码对于开发者来说几乎不可读,这极大地增加了调试和排错的难度。
- 性能影响:虽然大多数情况下性能影响很小,但某些情况下混淆可能导致微小的性能下降,特别是当使用了复杂的混淆规则时。
- 配置复杂:需要仔细配置混淆规则以确保关键代码不被错误地混淆,这可能需要一定的学习和测试。
- 兼容性问题:混淆可能破坏与反射、序列化等机制相关的兼容性,这些机制依赖于类的确切名称和签名。
可能出现的问题
- 反射问题:如果应用使用了Java反射API来访问类、方法或字段,混淆可能会改变这些成员的名称,导致反射失败。
- 序列化问题:如果应用涉及对象的序列化(如将对象保存到文件或数据库),混淆可能会破坏类的签名,导致序列化数据无法正确反序列化。
- 第三方库兼容:混淆可能破坏与第三方库的兼容性,特别是当这些库依赖于特定的类名或方法签名时。
- 调试困难:如前所述,混淆后的代码几乎不可读,使得调试过程变得非常困难。
结论
在决定是否在Spring Boot项目中使用Maven混淆插件时,需要权衡其优缺点以及可能带来的问题。如果你的项目特别关注安全性,并且愿意接受调试和维护方面的挑战,那么混淆可能是一个值得考虑的选择。然而,如果你的项目更注重开发效率和可维护性,或者不需要特别高的安全性保障,那么可能需要重新考虑是否使用混淆。
一、测试项目源码
二、引入maven proguard插件
<build>
<plugins>
<!-- proguard混淆插件-->
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<version>2.6.0</version>
<executions>
<execution>
<!-- 打包的时候开始混淆-->
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<proguardVersion>7.1.0</proguardVersion>
<injar>${project.build.finalName}.jar</injar>
<!--输出的jar-->
<outjar>${project.build.finalName}-pg.jar</outjar>
<!-- 是否混淆-->
<obfuscate>true</obfuscate>
<options>
<option>-target 1.8</option> <!--指定java版本号-->
<option>-dontshrink</option> <!--默认开启,不做收缩(删除注释、未被引用代码)-->
<option>-dontoptimize</option><!--默认是开启的,这里关闭字节码级别的优化-->
<option>-adaptclassstrings</option><!--混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代-->
<option>-useuniqueclassmembernames</option><!--# 对于类成员的命名的混淆采取唯一策略-->
<option>-keep class org.apache.logging.log4j.util.* { *; }</option>
<option>-dontwarn org.apache.logging.log4j.util.**</option>
<option>-keepattributes
Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
</option><!--对异常、注解信息在runtime予以保留,不然影响springboot启动-->
<!--不混淆所有interface接口-->
<!-- <option>-keepnames interface **</option>-->
<option>-keepclassmembers enum * { *; }</option><!--保留枚举成员及方法-->
<option>-keepparameternames</option>
<option>-keepclasseswithmembers public class * {
public static void main(java.lang.String[]);}
</option> <!--保留main方法的类及其方法名-->
<!--忽略note消息,如果提示javax.annotation有问题,那麽就加入以下代码-->
<option>-dontnote javax.annotation.**</option>
<option>-dontnote sun.applet.**</option>
<option>-dontnote sun.tools.jar.**</option>
<option>-dontnote org.apache.commons.logging.**</option>
<option>-dontnote javax.inject.**</option>
<option>-dontnote org.aopalliance.intercept.**</option>
<option>-dontnote org.aopalliance.aop.**</option>
<option>-dontnote org.apache.logging.log4j.**</option>
<!-- ##### 以下为需要根据项目情况修改 comment by 郭秀志 20200719 ##### -->
<!--入口程序类不能混淆,混淆会导致springboot启动不了-->
<option>-keep class com.example.basePro.BaseJavaProjectApplication</option>
<option>-keep class com.example.basePro.annotation.** {*;}</option>
<option>-keep class com.example.basePro.bean.** {*;}</option>
<!-- ##### 以上为需要根据项目情况修改 comment by 郭秀志 20200719 ##### -->
<option>-keep class org.springframework.boot.** { *; }</option>
<option>-keep interface * extends * { *; }</option>
<!--不混淆所有类,保存原始定义的注释-->
<option>-keepclassmembers class * {
@org.springframework.beans.factory.annotation.Autowired *;
@org.springframework.beans.factory.annotation.Value *;
}
</option>
</options>
<libs>
<!-- 添加依赖 java8-->
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jce.jar</lib>
<lib>${java.home}/lib/jsse.jar</lib>
</libs>
</configuration>
<dependencies>
<dependency>
<groupId>com.guardsquare</groupId>
<artifactId>proguard-base</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>com.guardsquare</groupId>
<artifactId>proguard-core</artifactId>
<version>7.1.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.18</version>
<configuration>
<mainClass>com.example.basePro.BaseJavaProjectApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
三、混淆结果
-pg后缀的是混淆之后的jar解压的文件,类名也被混淆了,同时生成两个文件proguard.map.txt、proguard_seed.txt
class文件内容记事本打开
class文件内容IDEA打开
PS D:\javaPro\java_baseProject\target> java -jar .\demo-0.0.1-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.7.RELEASE)
2024-08-27 08:56:58.207 INFO 26124 --- [ main] c.e.basePro.BaseJavaProjectApplication : Starting BaseJavaProjectApplication on LAPTOP-E5BC3
SCU with PID 26124 (D:\javaPro\java_baseProject\target\demo-0.0.1-SNAPSHOT.jar started by slh in D:\javaPro\java_baseProject\target)
2024-08-27 08:56:58.209 INFO 26124 --- [ main] c.e.basePro.BaseJavaProjectApplication : No active profile set, falling back to default prof
iles: default
2024-08-27 08:56:59.558 INFO 26124 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2024-08-27 08:56:59.581 INFO 26124 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2024-08-27 08:56:59.582 INFO 26124 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41]
2024-08-27 08:56:59.661 INFO 26124 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2024-08-27 08:56:59.661 INFO 26124 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization complete
d in 1383 ms
_ _ |_ _ _|_. ___ _ | _
| | |\/|_)(_| | |_\ |_)||_|_\
/ |
3.4.2
2024-08-27 08:57:00.368 INFO 26124 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecut
or'
2024-08-27 08:57:00.746 INFO 26124 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context
path ''
2024-08-27 08:57:00.763 INFO 26124 --- [ main] c.e.basePro.BaseJavaProjectApplication : Started BaseJavaProjectApplication in 2.996 seconds
(JVM running for 3.398)
2024-08-27 08:57:06.372 INFO 26124 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherSe
rvlet'
2024-08-27 08:57:06.372 INFO 26124 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2024-08-27 08:57:06.382 INFO 26124 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 10 ms
2024-08-27 08:57:06.461 INFO 26124 --- [nio-8080-exec-1] c.e.basePro.annotation.LogRecordAop : 操作者:diyunlong
2024-08-27 08:57:06.499 INFO 26124 --- [nio-8080-exec-1] c.e.basePro.annotation.LogRecordAop : 操作日志内容:123
2024-08-27 08:57:06.572 INFO 26124 --- [nio-8080-exec-1] c.e.basePro.annotation.LogRecordAop : 方法的执行开销是:73
四、在springboot中的使用
https://www.jb51.net/program/316587vdx.htm
看着是成功的:https://www.jianshu.com/p/09b0637525db
在Spring Boot项目中实现代码混淆可以通过一些工具实现,比如:ProGuard、yGuard、JGuard、DexGuard等。最常用的方法是ProGuard,它能够提供优秀的混淆规则以及与构建工具集成的方案,尤其是Maven和Gradle。
ProGuard是一个开源的Java类优化器和混淆器,它可以删除未使用的代码、优化字节码、并对类名、字段、和方法名进行混淆。使用ProGuard不仅增加了逆向工程的难度,还可以使编译后的应用程序更加轻巧。
五、坑与解决
https://docs.pingcode.com/ask/ask-ask/271442.html
https://blog.csdn.net/u014554286/article/details/129037346
https://blog.csdn.net/g5zhu5896/article/details/111320115
- 在混淆Spring Boot项目时,要特别注意Spring特有的一些特性,比如依赖注入、AOP、属性配置等,这些功能常常依赖于反射,而混淆可能会破坏这一机制。
<option>-keep class com.example.basePro.annotation.** {*;}</option>
对aop所在的包忽略混淆
- beanName冲突解决,修改主启动类中的beanName获取
package com.example.basePro;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class BaseJavaProjectApplication {
public static class CustomGenerator implements BeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName();
}
}
public static void main(String[] args) {
new SpringApplicationBuilder(BaseJavaProjectApplication.class)
.beanNameGenerator(new CustomGenerator())
.run(args);
}
}
- 在实施代码混淆的过程中,可能会遇到一些问题,例如混淆导致的运行时异常、第三方库兼容性问题等。此时,你可能需要调整混淆规则,添加相应的-keep选项来避免这些问题。
- 调整插件执行顺序:确保
proguard-maven-plugin
在maven-compiler-plugin
之后但在spring-boot-maven-plugin
之前运行。然而,由于spring-boot-maven-plugin
通常会处理整个应用程序的打包,包括将类文件和依赖项打包到一个可执行的jar中,因此直接在Spring Boot打包之前进行混淆可能不是最佳选择。 - 启动混淆包,无主清单问题,原因是混淆后的目录结构和springboot打包不同,缺少BOOT-INF目录,将混淆后的文件复制到项目jar\BOOT-INF\classes包内。然后再运行原始的jar包可正常运行。
针对上述问题,将输入数据的jar地址设置相同,给出如下解决方案:
<injar>${project.build.finalName}.jar</injar>
<!-- 输出jar名称,输入输出jar同名的时候就是覆盖,也是比较常用的配置。 -->
<outjar>${project.build.finalName}.jar</outjar>
最后结果:
- swagger与proguard之间的兼容性,未解决
有springfox包解决,未尝试https://www.jb51.net/program/316587vdx.htmhttps://www.jb51.net/program/316587vdx.htm
- 多模块之间依赖,未测试
- mybatis兼容性**,未测试**