jar包代码混淆

在maven项目中使用插件直接混淆之后再打包

修改 pom 文件

定义 proguard-maven-plugin 插件且插件位于 spring-boot-maven-plugin 插件的前面。

  • proguardInclude

表示使用外部扩展的配置文件 proguard.cfg,和 pom.xml 同目录

  • keepparameternames

此选项将保留所有原始方法参数,controller 如果函数的参数也混淆(如混淆为a、b、c等)会导致传参映射不上

详细配置如下(由于有详细注释,不再一一说明):

<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.2.2</proguardVersion>
                    <injar>${project.build.finalName}.jar</injar>
                    <!--输出的jar-->
                    <outjar>${project.build.finalName}.jar</outjar>
                    <!--是否混淆-->
                    <obfuscate>true</obfuscate>
                    <proguardInclude>${basedir}/proguard.cfg</proguardInclude>

                    <options>
                        <!--默认开启,不做收缩(删除注释、未被引用代码)-->
                        <option>-dontshrink</option>
                        <!--默认是开启的,这里关闭字节码级别的优化-->
                        <option>-dontoptimize</option>
                        <!--对于类成员的命名的混淆采取唯一策略-->
                        <option>-useuniqueclassmembernames</option>
                        <!--混淆时不生成大小写混合的类名,默认是可以大小写混合-->
                        <option>-dontusemixedcaseclassnames </option>
                        <!--混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代-->
                        <option>-adaptclassstrings</option>

                        <!--对异常、注解信息在runtime予以保留,不然影响springboot启动-->
                        <option>-keepattributes
                            Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
                        </option>

                        <!--此选项将保存接口中的所有原始名称(不混淆)-->
                        <option>-keepnames interface ** { *; }</option>
                        <!--此选项将保存所有软件包中的所有原始接口文件(不进行混淆)-->
                        <!--<option>-keep interface * extends * { *; }</option>-->

                        <!--此选项将保留所有原始方法参数,controller如果参数也混淆会导致传参映射不上  -->
                        <option>-keepparameternames</option>

                        <!--保留枚举成员及方法-->
                        <option>-keepclassmembers enum * { *; }</option>
                        <!--防止mybatis resultType 找不到bean-->
                        <option>-keep class cn.fuzhi.model.** { *; }</option>
                        <option>-keep class cn.fuzhi.config.** { *; }</option>
                        <option>-keep class cn.fuzhi.task.** { *; }</option>

                        <!--不混淆所有类,保存原始定义的注释-->
                        <!--<option>-keepclassmembers class * {
                            @org.springframework.context.annotation.Bean *;
                            @org.springframework.beans.factory.annotation.Autowired *;
                            @org.springframework.beans.factory.annotation.Value *;
                            @org.springframework.stereotype.Service *;
                            @org.springframework.stereotype.Component *;
                            }
                        </option>-->

                        <!--忽略warn消息-->
                        <option>-ignorewarnings</option>
                        <!--忽略note消息-->
                        <option>-dontnote</option>
                    </options>
                    <!--java 11-->
<!--                    <libs>-->
<!--                        <lib>${java.home}/jmods/</lib>-->
<!--                    </libs>-->
                    <!--java 8-->
                    <libs>
                         <lib>${java.home}/lib/rt.jar</lib>
                         <lib>${java.home}/lib/jsse.jar</lib>
                     </libs>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.guardsquare</groupId>
                        <artifactId>proguard-base</artifactId>
                        <version>7.2.2</version>
                    </dependency>
                </dependencies>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!--jar可直接运行-->
                    <executable>true</executable>
                </configuration>
            </plugin>
        </plugins>
    </build>

proguard.cfg

作为 pom.xml 中的扩展配置,详细配置如下:

#所有类(包括接口)的方法参数不混淆(包括没被keep的),如果参数混淆了,mybatis的mapper参数绑定会出错(如#{id})
-keepattributes MethodParameters

#入口程序类不能混淆,混淆会导致springboot启动不了
-keep class cn.fuzhi.Application {
        public static void main(java.lang.String[]);
     }

#mybatis的mapper/实体类不混淆,否则会导致xml配置的mapper找不到
-keep class cn.fuzhi.mapper.*
-keeppackagenames cn.fuzhi.mapper

#考虑到scanBasePackages,需要包名不被修改
-keeppackagenames cn.fuzhi

#一些配置类比如datasource,aopconfig如果混淆会导致各种启动报错
# 比如用@Pointcut("execution(public * com.langyastudio.edu.*.controller..*.*(..))")
# 指定webLog方法对应的@Pointcut作为切入点,所以包的名字不能修改
-keeppackagenames cn.fuzhi.controller.**
-keep class cn.fuzhi.task.*

#保留Serializable序列化的类不被混淆
#例如传入/输出的Bean属性
-keepclassmembers class * implements java.io.Serializable {*;}

#保留空的构造函数
-keepclassmembers class com.fuzhi.* {
 public <init>(...);
}

混淆配置要点

  • 建议逐个 java 包定义混淆规则,这样思路更清晰

  • repository(dao)层需要保存包名和类名,因为 Mybatis 的 xml 文件中引用了dao 层的接口

  • controller 层注意在使用 @PathVariable、@RequestParam 时需要显式声明参数名

  • dao 层用于映射数据库表的类和 controller 层映射前台参数的类,都需要保留类成员

  • 修改 spring 的 bean 命名策略,改成按类的全限定名来命名

  • 等等

 入口程序类

入口程序类不能混淆,混淆会导致 springboot 启动不了,增加如下配置:

-keep class cn.fuzhi.Application {
        public static void main(java.lang.String[]);
     }

bean 名称冲突

默认混淆后的类名为 xx.a.b、xx.c.a,直接使用混淆后的类名作为 bean 会引发重名异常,所以需要修改 BeanName 生成策略。

不能重写 generateBeanName 方法,因为有些 Bean 会自定义 BeanName,所以这些情况还需要走原来的逻辑。

public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class)
                .beanNameGenerator(new UniqueNameGenerator())
                .run(args);
//        SpringApplication.run(Application.class, args);
        log.error("============Web application Start Success====port:" + Global.System.SERVER_PORT + "===" + Global.System.EV + "=============");
    }

    @Component("UniqueNameGenerator")
    public @NotNull
    static class UniqueNameGenerator extends AnnotationBeanNameGenerator {

        @Override
        public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
            return Objects.requireNonNull(definition.getBeanClassName());
        }
    }

包名保留

  • 考虑到 scanBasePackages 等特殊的注解配置,需要包名不被修改,配置如下:

  • <!--此选项将保留所有原始方法参数,controller如果参数也混淆会导致传参映射不上  -->
    <option>-keepparameternames</option>
  • scanBasePackages 样例:

如果 cnfuzhi 名称被混淆,将导致 scanBasePackages 失效 

@SpringBootApplication(scanBasePackages = {"cn.fuzhi.*"})
public class Application
{
    public static void main(String[] args)
    {
                xxxxx
    }
}

配置类

一些配置类比如 datasource、aop、config 等如果混淆会导致各种启动报错

比如用 @Pointcut("execution(public * cn.fuzhi.controller.*.*(..))") 指定 ControllerAspect 方法对应的 @Pointcut 作为切入点。所以包的名字与函数名称不能修改

@Pointcut("execution(public * cn.fuzhi.controller.*.*(..)))")
public void ControllerAspect(){

}

dao 层

mybatis 的 mapper/实体类 不能混淆,否则会导致 xml 配置的 mapper 找不到

-keep class cn.fuzhi.mapper.*
-keeppackagenames cn.fuzhi.mapper
#接口类保留
xxxx

bean 属性保留

controller 层映射前台参数的类、后端返回的 bean 属性类等,不能混淆类的成员属性(如变成 string a;)

修改方案为保留 Serializable 序列化的类成员不被混淆

-keepclassmembers class * implements java.io.Serializable {*;}

bean 样例:

需要将原有的属性类增加 Serializable 的继承

@Data
@NoArgsConstructor
public class TokenVO implements Serializable
{
    /**
     * token
     */
    private String token;

    /**
     * token 前缀
     */
    private String tokenHead;
}

编译打包

mvnw clean package  -Dmaven.test.skip=true

混淆效果

用于进一步查看打包后的 jar 文件是否符合要求,同时还可以辅助查找 jar 文件运行失败的原因

常见的反编译工具使用 jd-gui。下载地址:http://java-decompiler.github.io/

直接通过 jd-gui 窗口打开编译打包后的 jar 文件即可。

混淆常见问题

混淆后自己全部代码没有被放入混淆后的jar包里(jar\BOOT-INF\classes 里面不包含com)

原因: proguard-maven-plugin插件放到了spring-boot-maven-plugin插件后面,应该是因为spring-boot-maven-plugin放在前面会先执行spring-boot-maven-plugin的repackage再执行proguard-maven-plugin的混淆,混淆后没有重新repackage

解决:把proguard-maven-plugin插件放到spring-boot-maven-plugin前面

DataSource,Aop,swagger等相关配置类混淆后导致的运行报错

原因:比如Aop是因为用@Around(value = "apiLog()")指定apiLog方法对应的@Pointcut作为切入点,但是因为apiLog方法被混淆成a导致找不到对应apiLog方法。就不一一列举了

解决:我是直接把所有配置类放到framework下面,然后proguard-maven-plugin配置让framework 下面的类和方法全部不混淆,这样省事点

-keep class cn.fuzhi.task.** {
    *;
}

aop事务控制,事务失效问题

原因:因为aop pointcut 是根据包路径和方法名(update,find)拦截,service 混淆后 包路径和方法名都变成 a,b导致拦截不到

解决:不混淆包路径和方法public方法名,如下

#不混淆service下的public方法名
-keepclassmembers class cn.fuzhi.service.** {public *** *(...);}
#不混淆service包名
-keeppackagenames cn.fuzhi.service.**

swagger 文档看不到api,以及接口请求参数controller 参数绑定报错

1)swagger 文档看不到api:因为我swagger配置的api 是通过.apis(RequestHandlerSelectors.basePackage(packageName))指定了具体包名,controller混淆后包名变成a,b,c所以扫描不到接口自然就生成不了文档

2)api接口请求出错:spring接口参数绑定是根据参数名称,混淆后参数名称会变成var1等,绑定的时候找不到原来定义的参数名就报错了.

解决:proguard-maven-plugin配置所有controller public 方法不参与混淆以及参数也不参与混淆,如下

 
#所有controller类的public方法你参与混淆
<option>-keepclassmembers class cn.fuzhi.controller.* {public *** *(...);}</option>
#不混淆controller的包路径
<option>-keeppackagenames cn.fuzhi.controller</option>
 
#参数不参与混淆
<option>-keepparameternames</option>

spring controller 参数绑定问题

原因:spring参数绑定有些是根据controller的方法参数名称绑定的,参数名称混淆后就会绑定不上

解决:

方法1:Controller用如@RequestParam的方式绑定

方法2:

①.配置-keepparameternames,这个参数可以让不被混淆的方法也不会混淆其参数,对interface无效.

<option>-keepparameternames</option>

②. 因为第①步的配置,那只要配置controller里的方法不混淆,那么方法参数也就不会混淆了,如下

-keepclassmembers class cn.fuzhi.controller.* {public *** *(...);}

mybatis 参数绑定报错org.apache.ibatis.binding.BindingException

原因:和第6点类似,因为方法参数被混淆,所以导致mybatis的mapper(是interface)定义的接口参数Id被混淆成 var1,mybatis xml #{id}绑定的时候只能找到var1于是BindingException.但是不能用第6点的方法2,因为keepparameternames 能让keep的class参数不混淆,却不能让interface的参数不参与混淆,而mybatis的mapper就是interface

解决:

方法1:mapper的参数 用 @Param("id") 强行绑定

方法2:此方法也可以解决第6点的问题

①. 配置 -keepattributes MethodParameters ,这个参数可以让不管有没有参与混淆的类的方法参数都不参与混淆,对类和接口都生效,但是这样会导致即使参与混淆的类也无法混淆参数

-keepattributes MethodParameters

②. 上面的配置是可以让所有方法参数不被proguard混淆,但是有些项目在java编译的时候却可能会对方法参数混淆(好像是针对interface方法参数的混淆,不针对class).所以可以配置编译时不混淆方法参数(-parameters),如下所示(这个根据自己项目实际情况配置,好像是和idea版本有关系)

   <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <compilerArgument>-parameters</compilerArgument>
                </configuration>
    </plugin>

CreateProcess error=206, 文件名或扩展名太长。

原因:因为window的cmd有长度限制,而proguard混淆时依赖太多的jar包导致命令行过长

解决:在proguard-maven-plugin的configuration中加入下面配置,这个配置会把jar包放到临时目录以便缩短命令行

<putLibraryJarsInTempDir>true</putLibraryJarsInTempDir>

<think>好的,我现在需要帮助用户解决在JDK 17和Spring Boot 3+环境下对JAR进行代码混淆的兼容性问题。首先,我得回顾用户之前的提问和我的回答,确保这次回答一致且补充新的信息。 用户提到参考了站内引用,其中引用了版本兼容性问题,特别是Spring Boot与其他组件的版本冲突。用户现在关心的是代码混淆时的兼容性,所以需要结合JDK 17和Spring Boot 3+的新特性来考虑。 首先,JDK 17引入了模块系统(JPMS),这可能会影响代码混淆工具对类的访问。此外,Spring Boot 3+需要Java 17,可能使用了新的语言特性,比如密封类(sealed classes)或记录类(records),这些在混淆时需要特别注意保留相关属性,否则可能导致运行时错误。 接下来,我需要考虑常用的代码混淆工具,如ProGuard、Allatori等,是否支持JDK 17和Spring Boot 3+。根据之前的回答,用户提到ProGuard的进阶规则需要调整,保留Spring Boot的注解和模块信息。同时,可能需要处理JDK 17的强封装机制,比如使用--add-opens参数来打开模块的访问权限。 另外,用户引用的资料中有提到Spring Boot配置文件中的路径匹配策略,这可能与某些库(如Swagger或Knife4j)的兼容性有关。混淆时如果重命名了相关类或方法,可能导致这些库无法正常工作,因此需要在混淆配置中排除这些部分。 还要考虑如何保留Spring Boot的自动配置类和Bean定义,避免因混淆导致Spring上下文初始化失败。例如,保留@SpringBootApplication、@Bean等注解的类和方法。 同时,用户之前提到的JAR加密工具如XJar和ClassFinal,可能需要与代码混淆工具配合使用,这时候需要确保两者的兼容性,不会互相干扰,比如加密后的类文件是否还能被正确混淆,或者混淆后的代码是否影响加密解密流程。 此外,用户可能遇到的具体问题,如启动时的IllegalAccessError,需要检查模块声明中的opens语句是否正确,确保反射调用的类在混淆后仍然可访问。混淆工具可能需要配置保留某些允许反射访问的或类。 最后,验证混淆后的JAR是否能够正常运行,可能需要使用反编译工具检查混淆效果,同时监控应用启动时的日志,排查类找不到或方法无法访问的错误。性能方面也需注意,混淆可能引入的开销是否在可接受范围内。 总结下来,解决方案需要涵盖选择合适的混淆工具,配置保留规则处理Spring和JDK 17的特性,处理模块化系统的访问问题,排除第三方库的混淆,以及验证和调试步骤确保兼容性。</think>以下是针对JDK 17和Spring Boot 3+的JAR代码混淆完整解决方案,结合引用内容与最新技术实践: --- ### 一、兼容性前置条件 #### 1. **版本匹配原则** - **Spring Boot 3.x** 强制要求 **JDK 17+**,且默认启用模块系统(JPMS)[^2] - **ProGuard 7.3+** 或 **Allatori 9.5+** 支持JDK 17语法特性(如密封类、记录类) - 需与 **Spring Native** 兼容(若使用GraalVM) #### 2. **依赖冲突规避** ```proguard # 保留Spring Boot 3核心机制(基于引用[3]经验) -keep @SpringBootApplication class * { *; } -keepclassmembers class * { @Bean *; @Autowired *; @Value *; @ConfigurationProperties *; } ``` --- ### 二、代码混淆深度配置 #### 1. **ProGuard进阶规则** ```proguard # JDK17特性保留(引用[1]兼容性问题延伸) -keep class javax.crypto.** { *; } # 加密模块必须保留 -keep class java.lang.invoke.StringConcatFactory { *; } -keepattributes PermittedSubclasses,Record # 密封类/记录类 # 保留Native调用接口(基于引用[4]的JNI需求) -keepclasseswithmembernames class * { native <methods>; } # Spring Boot Actuator端点保留 -keep class org.springframework.boot.actuate.endpoint.annotation.** { *; } ``` #### 2. **Allatori智能方案** ```xml <!-- 针对Spring Boot3的优化配置 --> <config> <property name="log_file" value="springboot3_obfuscation.log"/> <property name="string_encryption" value="true"/> <keep-names> <class annotation="org.springframework.boot.autoconfigure.SpringBootApplication"/> <class name="com.example.**.config.*"/> <!-- 保留配置类 --> </keep-names> </config> ``` --- ### 三、模块化系统特殊处理 #### 1. **module-info.java配置** ```java open module com.example.app { requires spring.boot.autoconfigure; requires spring.context; opens com.example.core to spring.core, org.hibernate.validator; opens com.example.config to spring.beans; } ``` **注意**:必须通过`opens`开放被混淆代码的反射访问权限 #### 2. **启动参数强化** ```bash # 解决模块访问冲突(引用[5]兼容性思路延伸) java --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.management/sun.management=ALL-UNNAMED \ -jar obfuscated-app.jar ``` --- ### 四、验证与调试方案 #### 1. **混淆效果验证** ```bash # 使用JDK17反编译验证 javap -p -c MyService.class | grep "obfuscated" # 检查保留的注解 jadx --deobf obfuscated.jar | grep "@SpringBootApplication" ``` #### 2. **运行时监控** ```java // 集成健康检查(引用[5]监控思路) @Bean public HealthIndicator obfuscationHealthCheck() { return () -> Health.status(checkClassMetadata()).build(); } private Status checkClassMetadata() { return MyService.class.isAnnotationPresent(Service.class) ? Status.UP : Status.DOWN; } ``` --- ### 五、典型问题解决方案 | 问题现象 | 解决方案 | 原理关联 | |---------|----------|----------| | `NoSuchMethodError` | 在ProGuard中保留方法签名 | 引用[1]版本冲突延伸 | | `InaccessibleObjectException` | 添加`--add-opens`参数 | JPMS强封装特性[^2] | | Swagger/Knife4j失效 | 保留`springfox` | 引用[3]路径匹配经验 | | JNI调用失败 | 保留native方法并指定`.dll`加载路径 | 引用[4] JNI规范 | --- ### 六、工具链推荐组合 1. **基础方案** ProGuard 7.3 + Maven插件 + Spring Boot Module Tools 2. **企业级方案** Allatori 9.5 + ClassFinal(加密) + JPMS Helper 3. **云原生方案** GraalVM Native Image + Obfuscator4J(支持AOT混淆) --- ### 附:Spring Boot 3.x混淆兼容表 | 组件 | 保留规则 | 配置示例 | |------|---------|----------| | Spring Data JPA | 保留实体类注解 | `-keep @Entity class *` | | Spring Security | 保留Filter链 | `-keep class * extends OncePerRequestFilter` | | Spring Cloud | 保留Bootstrap上下文 | `-keep class org.springframework.cloud.bootstrap.**` |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱钓鱼的程序员小郭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值