写点什么

SpringBoot 多 Module Proguard 混淆(Gradle)

作者:Java你猿哥
  • 2023-04-14
    湖南
  • 本文字数:3660 字

    阅读完需:约 12 分钟

由于项目需求,需要对项目代码做个混淆

在做的过程中发现,搜索到的大部分帖子都是单 Module 的和 Maven 项目的,有一定的借鉴意义,但还是不能直接解决问题。经过一段时间的试错之后,总算把项目代码混淆成功,并运行起来了,在此就做个总结,希望能对也有此需求的大家有所帮助。

1、Proguard 介绍

ProGuard 是一个压缩、优化和混淆 Java 字节码文件的免费的工具,它可以删除无用的类、字段、方法和属性。可以删除没用的注释,最大限度地优化字节码文件。它还可以使用简短的无意义的名称来重命名已经存在的类、字段、方法和属性。常常用于 Android 开发用于混淆最终的项目,增加项目被反编译的难度。

ProGuard 处理流程:

  • 压缩(Shrink):检测并删除未使用的类,字段,方法和属性。

  • 优化(Optimize):分析并优化方法的字节码。

  • 混淆(Obfuscate): 使用简短的无意义名称例如 a,b,c 等,重命名类,字段和方法。

  • 预检(Preveirfy):主要是在 Java 平台上对处理后的代码进行预检。

需要在此说明的是,Proguard 只是增加了反编译的难度,并不是真正的加密。

2、SpringBoot 多 Module Gradle 集成

2.1、引入插件

在项目启动的 Module 的 build.gradle 下引入插件

build.gradle 参考结构

import proguard.gradle.ProGuardTask
buildscript { repositories { mavenCentral() } dependencies { classpath("com.guardsquare:proguard-gradle:7.3.2") }}
task proguard(type: ProGuardTask) {}复注:ProGuard的版本对JDK的版本有限制,具体可参考官方文档。由于我们项目使用的是JDK17,所以使用的是7.3.2,具体项目具体配置
复制代码

2.2、规则配置

在上述的 build.gradle 同级目录下,创建规则文件

proguard.pro

#指定Java的版本-target 17#proguard会对代码进行优化压缩,他会删除从未使用的类或者类成员变量等-dontshrink#是否关闭字节码级别的优化,如果不开启则设置如下配置-dontoptimize#混淆时不生成大小写混合的类名,默认是可以大小写混合-dontusemixedcaseclassnames# 对于类成员的命名的混淆采取唯一策略-useuniqueclassmembernames#混淆时不生成大小写混合的类名,默认是可以大小写混合-dontusemixedcaseclassnames#混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代-adaptclassstrings
#对异常、注解信息予以保留-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod# 此选项将保存接口中的所有原始名称(不混淆)-->-keepnames interface ** { *; }# 此选项将保存所有软件包中的所有原始接口文件(不进行混淆)#-keep interface * extends * { *; }#保留参数名,因为控制器,或者Mybatis等接口的参数如果混淆会导致无法接受参数,xml文件找不到参数-keepparameternames# 保留枚举成员及方法-keepclassmembers enum * { *; }# 不混淆所有类,保存原始定义的注释--keepclassmembers class * { @org.springframework.context.annotation.Bean *; @org.springframework.beans.factory.annotation.Autowired *; @org.springframework.beans.factory.annotation.Value *; @org.springframework.stereotype.Service *; @javax.persistence.Table *; @javax.persistence.Entity *; }

-keepclasseswithmembers public class * { public static void main(java.lang.String[]);} ##保留main方法的类及其方法名-keep public class ch.qos.logback.**{*;}-keep class com.fasterxml.jackson.** { *; }-keep public class com.fasterxml.jackson.** { *; }
#忽略warn消息-ignorewarnings#忽略note消息-dontnote#打印配置信息-printconfiguration
复制代码

SpringBoot 修改

public class xxApplication {
public static void main(String[] args) { new SpringApplicationBuilder(xxApplication .class) .beanNameGenerator(new CustomBeanNameGenerator()) .run(args); }
private static class CustomBeanNameGenerator implements BeanNameGenerator { @Override public String generateBeanName(BeanDefinition d, BeanDefinitionRegistry r) { return d.getBeanClassName(); } }}
复制代码

注:配置文件是混淆过程中最容易出错的地方,很多时候不容易把握住什么时候该 keep,这里简单总结一下

1、涉及全路径的以及较底层类应该保留,如使用了反射或者切面等等

2、对外公开的类应该保留

2.3、Gradle Task

以上涉及的部分单 Module 和多 Module 都没有区别,这部分是真正实现 SpringBoot 多 Module 混淆的部分。

在最开始配置的时候,由于没有配置其他 Module,导致最后构建的 Jar 包只包含启动类。

以下还是在上述的 build.gradle 上做修改,只涉及到 ProGuard

build.gradle

import proguard.gradle.ProGuardTask
buildscript {
repositories { mavenCentral() } dependencies { classpath("com.guardsquare:proguard-gradle:7.3.2") }
}
dependencies { implementation project(':xx:a') implementation project(':xx:b') implementation project(':xx:c') implementation project(':xx:d') implementation('com.guardsquare:proguard-gradle:7.3.2')}
task proguard(type: ProGuardTask) { // 输出混淆前->混淆后的映射 printmapping "$buildDir/mapping.txt" // 混淆规则文件 configuration 'proguard.pro'
// 混淆时依赖的库 libraryjars configurations.runtimeClasspath.collect() // jdk 依赖,区分jdk8 前后版本 if (System.getProperty('java.version').startsWith('1.')) { libraryjars "${System.getProperty('java.home')}/lib/rt.jar" } else { libraryjars "${System.getProperty('java.home')}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class' libraryjars "${System.getProperty('java.home')}/jmods/java.desktop.jmod", jarfilter: '!**.jar', filter: '!module-info.class' }
// 混淆输入 //class 混淆 injars sourceSets.main.output
injars "${project(":xx:a").buildDir}\classes\java\main" injars "${project(":xx:b").buildDir}\classes\java\main" injars "${project(":xx:c").buildDir}\classes\java\main" injars "${project(":xx:d").buildDir}\classes\java\main"

// 混淆输出 outjars "$buildDir/classes-pro"}
// 清除现有的lib目录task clearJar(type: Delete) { delete "$buildDir\libs\lib"}
// 拷贝配置文件task copyConfigFile(type: Copy) { // 清除现有的配置目录 delete "$buildDir\libs\config" from 'src/main/resources' into 'build/libs/config'}
// 将依赖包复制到lib目录task copyJar(type: Copy, dependsOn: 'clearJar') { from configurations.compileClasspath into "$buildDir\libs\lib"}

task clearSdkJar(type: Delete) { delete "$buildDir\libs\sdk.jar"}
tasks.register('makeJar', Jar) { //指定生成的jar名 baseName 'xx' from sourceSets.main.output // lib目录的清除和复制任务 dependsOn clearJar dependsOn copyJar
// 指定依赖包的路径 manifest { attributes "Manifest-Version": 1.0, 'Main-Class': 'xx.xx.xx', 'Class-Path': configurations.compileClasspath.files.collect { "lib/$it.name" }.join(' ') }
}
tasks.register('makeProJar', Jar) {
dependsOn makeJar dependsOn proguard
//指定生成的jar名 baseName 'xx-pro' //从哪里打包class文件 from("$buildDir/classes-pro")
dependsOn copyJar
// 指定依赖包的路径 manifest { attributes "Manifest-Version": 1.0, 'Main-Class': 'xx.xx.xx', 'Class-Path': configurations.compileClasspath.files.collect { "lib/$it.name" }.join(' ') }
doLast { delete 'build/libs/lib/com' }
}
复制代码

注:proguard 中的 injars 是实现多 Module 混淆的关键,通过 injars 将其他 Module 的代码混淆到指定目录

3、验证

执行 gradle clean

执行 gradle makeProJar

最后在 libs 目录下即可看到原 Jar 包和混淆过后的 Jar 包,lib 目录下为依赖的类库


启动验证:java -jar -Dloader.path=lib xx-pro-1.0-SNAPSHOT.jar

启动不成功的话,可能是 proguard.pro 配置的不合理,需反复检查验证

用户头像

Java你猿哥

关注

一只在编程路上渐行渐远的程序猿 2023-03-09 加入

关注我,了解更多Java、架构、Spring等知识

评论

发布
暂无评论
SpringBoot 多Module Proguard混淆(Gradle)_spring_Java你猿哥_InfoQ写作社区