0


springboot+proguard+maven 实现代码混淆 看这一篇就够了

使用

proguard

混淆代码只能增加阅读和理解的难度, 并不能百分百保证代码安全。常用的应用场景是项目需要部署到客户机器上,一定程度上防止代码泄露。

proguard 简介

ProGuard 是一个混淆代码的开源项目,它的主要作用是混淆代码,ProGuard 包括以下 4 个功能:

  • 压缩(Shrink):检测并移除代码中无用的类、字段、方法和特性(Attribute)
  • 优化(Optimize):对字节码进行优化,移除无用的指令
  • 混淆(Obfuscate):使用 a,b,c,d 这样简短而无意义的名称,对类、字段和方法进行重命名
  • 预检(Preveirfy):在 Java 平台上对处理后的代码进行预检,确保加载的 class 文件是可执行的

img

实战示例

proguard 是广为使用的工具之一,可是用他的客户端方式来混淆 springboot 项目的时候最后总得不到可执行的 jar。后来发现了

proguard-maven-plugin

这个插件,所有 proguard 的指令都可以在 pom 中进行定义实现,本文基于

springboot2.x + maven + proguard

架构进行代码混淆。

源码:

https://github.com/hacfins/spring-boot-2-api

修改 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>${proguard-maven-plugin.version}</version><executions><execution><!--打包的时候开始混淆--><phase>package</phase><goals><goal>proguard</goal></goals></execution></executions><configuration><proguardVersion>${proguard.version}</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><!--不混淆所有类,保存原始定义的注释--><!--<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>${proguard.version}</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 com.langyastudio.edu.admin.Application {
        public static void main(java.lang.String[]);
     }

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

#考虑到scanBasePackages,需要包名不被修改
-keeppackagenames com.langyastudio.edu
-keeppackagenames com.langyastudio.edu.admin.common

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

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

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

混淆配置要点

  • 建议逐个 java 包定义混淆规则,这样思路更清晰
  • repository(dao)层需要保存包名和类名,因为 Mybatis 的 xml 文件中引用了dao 层的接口
  • controller 层注意在使用 @PathVariable、@RequestParam 时需要显式声明参数名
  • dao 层用于映射数据库表的类和 controller 层映射前台参数的类,都需要保留类成员
  • 修改 spring 的 bean 命名策略,改成按类的全限定名来命名
  • 等等

入口程序类

  • 入口程序类不能混淆,混淆会导致 springboot 启动不了,增加如下配置:
-keep class com.langyastudio.edu.admin.Application {
        public static void main(java.lang.String[]);
     }

bean 名称冲突

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

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

publicclassApplication{publicstaticvoidmain(String[] args){newSpringApplicationBuilder(Application.class).beanNameGenerator(newUniqueNameGenerator()).run(args);}/**
     * 由于需要混淆代码,混淆后类都是A B C,spring 默认是把A B C当成BeanName,BeanName又不能重复导致报错
     * 所以需要重新定义BeanName生成策略
     */@Component("UniqueNameGenerator")publicstaticclassUniqueNameGeneratorextendsAnnotationBeanNameGenerator{/**
         * 重写buildDefaultBeanName
         * 其他情况(如自定义BeanName)还是按原来的生成策略,只修改默认(非其他情况)生成的BeanName带上包名
         */@Overridepublic@NotNullStringbuildDefaultBeanName(BeanDefinition definition){//全限定类名returnObjects.requireNonNull(definition.getBeanClassName());}}}

包名保留

  • 考虑到 scanBasePackages 等特殊的注解配置,需要包名不被修改,配置如下:
-keeppackagenames com.langyastudio.edu
  • scanBasePackages 样例:

如果

com.langyastudio.edu

名称被混淆,将导致

scanBasePackages

失效

@SpringBootApplication(scanBasePackages ={"com.langyastudio.edu.*"})publicclassApplication{publicstaticvoidmain(String[] args){
        xxxxx
    }}

配置类

  • 一些配置类比如 datasource、aop、config 等如果混淆会导致各种启动报错比如用 @Pointcut("execution(public * com.langyastudio.edu.*.controller..*.*(..))") 指定 webLog 方法对应的 @Pointcut 作为切入点。所以包的名字与函数名称不能修改-keeppackagenames com.langyastudio.edu.*.controller.**-keep class com.langyastudio.edu.admin.config.*
  • 配置类样例:publicclassWebLogAspect{/** * 包及其子包下所有类中的所有方法都应用切面里的通知 */@Pointcut("execution(public * com.langyastudio.edu.*.controller..*.*(..))")publicvoidwebLog(){}@Before("webLog()")publicvoiddoBefore(JoinPoint joinPoint)throwsThrowable{}}

dao 层

  • mybatis 的 mapper/实体类 不能混淆,否则会导致 xml 配置的 mapper 找不到-keep class com.langyastudio.edu.admin.dao.*-keeppackagenames com.langyastudio.edu.admin.dao#接口类保留xxxx

bean 属性保留

  • controller 层映射前台参数的类、后端返回的 bean 属性类等,不能混淆类的成员属性(如变成 string a;
  • 修改方案为保留 Serializable 序列化的类成员不被混淆-keepclassmembers class * implements java.io.Serializable {*;}
  • bean 样例:需要将原有的属性类增加 Serializable 的继承
@Data@NoArgsConstructorpublicclassTokenVOimplementsSerializable{/**
     * token
     */privateString token;/**
     * token 前缀
     */privateString tokenHead;}

编译打包

打包成功一定要运行 编译后的 jar ,测试是否能否正常运行

mvnw clean package  -Dmaven.test.skip=true

image-20220327220710651

混淆效果

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

常见的反编译工具使用

jd-gui

。下载地址:http://java-decompiler.github.io/。

直接通过

jd-gui

窗口打开编译打包后的 jar 文件即可。

image-20220327221048292

参考文档

ProGuard manual

Code obfuscation for Spring Boot applications using the ProGuard plugin proguard-maven-plugin

springboot proguard 代码混淆


本文转载自: https://blog.csdn.net/aoshilang2249/article/details/123783160
版权归原作者 郎涯技术 所有, 如有侵权,请联系我们删除。

“springboot+proguard+maven 实现代码混淆 看这一篇就够了”的评论:

还没有评论