4

本文主要研究下如何使用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参数进行调整。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论