springboot+proguard+maven实现代码混淆看这⼀篇就够了
使⽤ proguard 混淆代码只能增加阅读和理解的难度, 并不能百分百保证代码安全。常⽤的应⽤场景是项⽬需要部署到客户机器上,⼀定程度上防⽌代码泄露。
proguard 简介
ProGuard 是⼀个混淆代码的开源项⽬,它的主要作⽤是混淆代码,ProGuard 包括以下 4 个功能:
压缩(Shrink):检测并移除代码中⽆⽤的类、字段、⽅法和特性(Attribute)
优化(Optimize):对字节码进⾏优化,移除⽆⽤的指令
混淆(Obfuscate):使⽤ a,b,c,d 这样简短⽽⽆意义的名称,对类、字段和⽅法进⾏重命名
预检(Preveirfy):在 Java 平台上对处理后的代码进⾏预检,确保加载的 class ⽂件是可执⾏的
实战⽰例
proguard 是⼴为使⽤的⼯具之⼀,可是⽤他的客户端⽅式来混淆 springboot 项⽬的时候最后总得不到可执⾏的 jar。后来发现了proguard-maven-plugin 这个插件,所有 proguard 的指令都可以在 pom 中进⾏定义实现,本⽂基于 springboot2.x + maven + proguard 架构进⾏代码混淆。
源码:
修改 pom ⽂件
定义 proguard-maven-plugin 插件且插件位于 spring-boot-maven-plugin 插件的前⾯。
proguardInclude
表⽰使⽤外部扩展的配置⽂件 proguard.cfg,和 l 同⽬录
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 * {
@t.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/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
作为 l 中的扩展配置,详细配置如下:
#所有类(包括接⼝)的⽅法参数不混淆(包括没被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.adminmon
#⼀些配置类⽐如datasource,aopconfig如果混淆会导致各种启动报错
# ⽐如⽤@Pointcut("execution(public * com.langyastudio.edu.*.controller..*.*(..))")
# 指定webLog⽅法对应的@Pointcut作为切⼊点,所以包的名字不能修改
-keeppackagenames com.langyastudio.edu.*.controller.**
-keep class com.langyastudio.fig.*
#保留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,所以这些情况还需要⾛原来的逻辑。
public class Application
{
public static void main(String[] args)
{
new SpringApplicationBuilder(Application.class)
.beanNameGenerator(new UniqueNameGenerator())
.run(args);
}
/**
* 由于需要混淆代码,混淆后类都是A B C,spring 默认是把A B C当成BeanName,BeanName⼜不能重复导致报错
* 所以需要重新定义BeanName⽣成策略
*/
@Component("UniqueNameGenerator")
public static class UniqueNameGenerator extends AnnotationBeanNameGenerator
{
/**
* 重写buildDefaultBeanName
* 其他情况(如⾃定义BeanName)还是按原来的⽣成策略,只修改默认(⾮其他情况)⽣成的BeanName带上包名
*/
@Override
public@NotNull String buildDefaultBeanName(BeanDefinition definition)
{
//全限定类名
BeanClassName());
}
}
}
包名保留
考虑到 scanBasePackages 等特殊的注解配置,需要包名不被修改,配置如下:
-keeppackagenames com.langyastudio.edu
scanBasePackages 样例:
如果 com.langyastudio.edu 名称被混淆,将导致 scanBasePackages 失效
@SpringBootApplication(scanBasePackages ={"com.langyastudio.edu.*"})
springboot aoppublic class Application
{
public static void main(String[] args)
{
xxxxx
}
}
配置类
⼀些配置类⽐如 datasource、aop、config 等如果混淆会导致各种启动报错
⽐如⽤ @Pointcut("execution(public * com.langyastudio.edu.*.controller..*.*(..))") 指定 webLog ⽅法对应的 @Pointcut 作为切⼊点。所以包的名字与函数名称不能修改
-keeppackagenames com.langyastudio.edu.*.controller.**
-keep class com.langyastudio.fig.*
配置类样例:
public class WebLogAspect
{
/**
* 包及其⼦包下所有类中的所有⽅法都应⽤切⾯⾥的通知
*/
@Pointcut("execution(public * com.langyastudio.edu.*.controller..*.*(..))")
public void webLog()
{
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint)throws Throwable
{
}
}
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 的继承
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论