序
本文主要研究下如何使用proguard混淆springboot程式碼
maven
<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>5.3.3</proguardVersion>
<injar>${project.build.finalName}.jar</injar>
<outjar>${project.build.finalName}.jar</outjar>
<obfuscate>true</obfuscate>
<proguardInclude>${project.basedir}/proguard.cfg</proguardInclude>
<libs>
<!-- Include main JAVA library required.-->
<lib>${java.home}/lib/rt.jar</lib>
<!-- Include crypto JAVA library if necessary.-->
<lib>${java.home}/lib/jce.jar</lib>
</libs>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>5.3.3</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>com.example.demo.MvcDemoApplication</mainClass>
</configuration>
</execution>
</executions>
</plugin>
複製程式碼
這裡引用了com.github.wvengen的proguard-maven-plugin外掛,使用的proguard-base版本是5.3.3
這裡使用java8,因此libs那裡照常配置rt.jar,jce.jar,如果是java9的話,則需要換成相應的模組。
另外指定proguard的階段為package,springboot打包在repackage階段
proguard.cfg
主要的重點在這個proguard.cfg配置
-target 1.8 ##指定java版本號
-dontshrink ##預設是開啟的,這裡關閉shrink,即不刪除沒有使用的類/成員
-dontoptimize ##預設是開啟的,這裡關閉位元組碼級別的優化
-useuniqueclassmembernames ##對於類成員的命名的混淆採取唯一策略
-adaptclassstrings ## 混淆類名之後,對使用Class.forName(`className`)之類的地方進行相應替代
-dontusemixedcaseclassnames ## 混淆時不生成大小寫混合的類名,預設是可以大小寫混合
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod ##對異常、註解資訊在runtime予以保留,不然影響springboot啟動
-keepclasseswithmembers public class * { public static void main(java.lang.String[]);} ##保留main方法的類及其方法名
-keepclassmembers enum * { *; } ##保留列舉成員及方法
複製程式碼
- proguard引數太多,謹慎起見,這裡關閉shrink,關閉optimize,從基本的配置起
- 有2點要注意,1就是要保留main方法的類及方法名,不然啟動不了;2就是要通過keepattributes保留註解等原資訊,不然影響springboot啟動
- 這裡因為示例工程用到了列舉,所以也保留了列舉
bean命名重複異常
由於proguard混淆貌似不能指定在basePackages下面類名混淆後唯一,不同包名經常有a.class,b.class,c.class之類重複的類名,因此spring容器初始化bean的時候會報錯。
異常資訊
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.example.demo.MvcDemoApplication]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name `a` for bean class [com.example.demo.c.a] conflicts with existing, non-compatible bean definition of same name and class [com.example.demo.b.a]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:181) ~[spring-context-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:308) ~[spring-context-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228) ~[spring-context-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:270) ~[spring-context-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:93) ~[spring-context-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) ~[spring-context-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) ~[spring-context-4.3.10.RELEASE.jar!/:4.3.10.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.5.RELEASE.jar!/:1.5.5.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.5.RELEASE.jar!/:1.5.5.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.5.RELEASE.jar!/:1.5.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.5.RELEASE.jar!/:1.5.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.5.RELEASE.jar!/:1.5.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.5.RELEASE.jar!/:1.5.5.RELEASE]
at com.example.demo.MvcDemoApplication.main(MvcDemoApplication.java:10) [classes!/:0.0.1-SNAPSHOT]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_151]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_151]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_151]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_151]
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) [app.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) [app.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) [app.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) [app.jar:0.0.1-SNAPSHOT]
複製程式碼
更換bean命名策略
慶幸的是,我們可以通過改變spring的bean的命名策略來解決這個問題,把包名帶上,就唯一了
@SpringBootApplication
public class MvcDemoApplication {
public static class CustomGenerator implements BeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName();
}
}
public static void main(String[] args) {
new SpringApplicationBuilder(MvcDemoApplication.class)
.beanNameGenerator(new CustomGenerator())
.run(args);
}
}
複製程式碼
至此大功告成
小結
本文的proguard配置僅僅是根據自身工程的情況來量身定做的,不是通用的,具體的場景還需要根據具體情況對proguard引數進行調整。