本文主要研究下如何使用proguard-maven-plugin插件混淆springboot代碼。工程代碼是實際跑在線上的Springboot2.x項目,踩過N個坑,最后實測成功。
先說貼出成功的配置
<build>
<finalName>spring</finalName>
<resources>
<resource>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/application-*.yml</exclude>
</excludes>
</resource>
</resources>
<plugins>
<!--代碼混淆-->
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals><goal>proguard</goal></goals>
</execution>
</executions>
<configuration>
<proguardVersion>6.0.3</proguardVersion>
<injar>${project.build.finalName}.jar</injar>
<!-- <injar>classes</injar> -->
<outjar>${project.build.finalName}.jar</outjar>
<obfuscate>true</obfuscate>
<options>
<!-- ##默認是開啟的,這里關閉shrink,即不刪除沒有使用的類/成員-->
<option>-dontshrink</option>
<!-- ##默認是開啟的,這里關閉字節碼級別的優化-->
<option>-dontoptimize</option>
<!--##對於類成員的命名的混淆采取唯一策略-->
<option>-useuniqueclassmembernames</option>
<!--- 混淆類名之后,對使用Class.forName('className')之類的地方進行相應替代-->
<option>-adaptclassstrings </option>
<option>-ignorewarnings</option>
<!-- 混淆時不生成大小寫混合的類名,默認是可以大小寫混合-->
<option>-dontusemixedcaseclassnames</option>
<!-- This option will replace all strings in reflections method invocations with new class names.
For example, invokes Class.forName('className')-->
<!-- <option>-adaptclassstrings</option> -->
<!-- This option will save all original annotations and etc. Otherwise all we be removed from files.-->
<!-- 不混淆所有特殊的類-->
<option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>
<!-- This option will save all original names in interfaces (without obfuscate).-->
<option>-keepnames interface **</option>
<!-- This option will save all original methods parameters in files defined in -keep sections,
otherwise all parameter names will be obfuscate.-->
<option>-keepparameternames</option>
<!-- <option>-libraryjars **</option> -->
<!-- This option will save all original class files (without obfuscate) but obfuscate all in domain package.-->
<!--<option>-keep class !com.slm.proguard.example.spring.boot.domain.** { *; }</option>-->
<option>-keep class !com.dsys.project.** { *; }</option>
<option>-keep class com.dsys.project.App { *; }</option>
<option>-keep class com.dsys.project.config.** { *; }</option>
<!--保留不然Mybatis報錯-->
<option>-keep class com.dsys.project.entity.** { *; }</option>
<option>-keep class com.dsys.project.utils.PageRes { *; }</option>
<option>-keep class com.dsys.project.controller.** { *; }</option>
<option>-keep class com.dsys.project.mp.controller.** { *; }</option>
<option>-keep class com.dsys.project.mp.config.** { *; }</option>
<option>-keep class com.dsys.project.dto.** { *; }</option>
<option>-keep class * implements java.io.Serializable </option>
<!-- This option will save all original class files (without obfuscate) in service package-->
<!--<option>-keep class com.slm.proguard.example.spring.boot.service { *; }</option>-->
<!-- This option will save all original interfaces files (without obfuscate) in all packages.-->
<option>-keep interface * extends * { *; }</option>
<!-- <option>-keep @org.springframework.stereotype.Service class *</option> -->
<!-- This option will save all original defined annotations in all class in all packages.-->
<option>-keepclassmembers class * {
<!-- @org.springframework.beans.factory.annotation.Autowired *; -->
@org.springframework.beans.factory.annotation.Value *;
}
</option>
</options>
<libs>
<!-- Include main JAVA library required.-->
<lib>${java.home}/lib/rt.jar</lib>
</libs>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>6.2.2</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<!-- <phase>none</phase> -->
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>com.dsys.project.App</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
主要的坑,springboot項目配置注意項
啟動類不能混淆
//混淆會把Bean的名稱重復,這里要求SpringBoot生成唯一的BeanName
@SpringBootApplication
@MapperScan("com.dsys.project.dao")
@ComponentScan("com.dsys")
@ServletComponentScan("com.dsys")
@EnableCaching
@EnableAsync
public class App {
public static class CustomGenerator implements BeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName();
}
}
/***
* 由於proguard混淆貌似不能指定在basePackages下面類名混淆后唯一,不同包名經常有a.class,b.class,c.class之類重復的類名,因此spring容器初始化bean的時候會報錯。
*慶幸的是,我們可以通過改變spring的bean的命名策略來解決這個問題,把包名帶上,就唯一了
* @param args
*/
public static void main(String[] args) {
new SpringApplicationBuilder(App.class)
.beanNameGenerator(new CustomGenerator())
.run(args);
}
}
實體類一定要保留
解釋:這里的實體類包括各種Entity,Dto等。保留的原因有:
1. Mybatis的XML的ResultType需要實體類的全路徑
2. Jackson需要序列化,字段混淆前端會找不到
Controller一定要保留
解釋:Controller混淆了前端找不到請求路徑,模板引擎例如thymeleaf找不到路徑
SpringBoot JavaConfig配置不能混淆
//解釋:例如下面的JavaConfig配置,混淆后配置出錯
@Data
@ConfigurationProperties(prefix = "wx.mp")
public class WxMpProperties {
private List<MpConfig> configs;
@Data
public static class MpConfig {
private String appId;
}
}
其他配置參考以上的注釋,其他照抄修改成自己的項目對應路徑即可,使用JD-GUI反編譯查看效果。