当前位置: 首页 > news >正文

jar包代码混淆

在maven项目中使用插件直接混淆之后再打包

修改 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>2.6.0</version>
                <executions>
                    <execution>
                        <!--打包的时候开始混淆-->
                        <phase>package</phase>
                        <goals>
                            <goal>proguard</goal>
                        </goals>
                    </execution>
                </executions>

                <configuration>
                    <proguardVersion>7.2.2</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>
                        <!--防止mybatis resultType 找不到bean-->
                        <option>-keep class cn.fuzhi.model.** { *; }</option>
                        <option>-keep class cn.fuzhi.config.** { *; }</option>
                        <option>-keep class cn.fuzhi.task.** { *; }</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>7.2.2</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 cn.fuzhi.Application {
        public static void main(java.lang.String[]);
     }

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

#考虑到scanBasePackages,需要包名不被修改
-keeppackagenames cn.fuzhi

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

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

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

混淆配置要点

  • 建议逐个 java 包定义混淆规则,这样思路更清晰

  • repository(dao)层需要保存包名和类名,因为 Mybatis 的 xml 文件中引用了dao 层的接口

  • controller 层注意在使用 @PathVariable、@RequestParam 时需要显式声明参数名

  • dao 层用于映射数据库表的类和 controller 层映射前台参数的类,都需要保留类成员

  • 修改 spring 的 bean 命名策略,改成按类的全限定名来命名

  • 等等

 入口程序类

入口程序类不能混淆,混淆会导致 springboot 启动不了,增加如下配置:

-keep class cn.fuzhi.Application {
        public static void main(java.lang.String[]);
     }

bean 名称冲突

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

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

public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class)
                .beanNameGenerator(new UniqueNameGenerator())
                .run(args);
//        SpringApplication.run(Application.class, args);
        log.error("============Web application Start Success====port:" + Global.System.SERVER_PORT + "===" + Global.System.EV + "=============");
    }

    @Component("UniqueNameGenerator")
    public @NotNull
    static class UniqueNameGenerator extends AnnotationBeanNameGenerator {

        @Override
        public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
            return Objects.requireNonNull(definition.getBeanClassName());
        }
    }

包名保留

  • 考虑到 scanBasePackages 等特殊的注解配置,需要包名不被修改,配置如下:

  • <!--此选项将保留所有原始方法参数,controller如果参数也混淆会导致传参映射不上  -->
    <option>-keepparameternames</option>
  • scanBasePackages 样例:

如果 cnfuzhi 名称被混淆,将导致 scanBasePackages 失效 

@SpringBootApplication(scanBasePackages = {"cn.fuzhi.*"})
public class Application
{
    public static void main(String[] args)
    {
                xxxxx
    }
}

配置类

一些配置类比如 datasource、aop、config 等如果混淆会导致各种启动报错

比如用 @Pointcut("execution(public * cn.fuzhi.controller.*.*(..))") 指定 ControllerAspect 方法对应的 @Pointcut 作为切入点。所以包的名字与函数名称不能修改

@Pointcut("execution(public * cn.fuzhi.controller.*.*(..)))")
public void ControllerAspect(){

}

dao 层

mybatis 的 mapper/实体类 不能混淆,否则会导致 xml 配置的 mapper 找不到

-keep class cn.fuzhi.mapper.*
-keeppackagenames cn.fuzhi.mapper
#接口类保留
xxxx

bean 属性保留

controller 层映射前台参数的类、后端返回的 bean 属性类等,不能混淆类的成员属性(如变成 string a;)

修改方案为保留 Serializable 序列化的类成员不被混淆

-keepclassmembers class * implements java.io.Serializable {*;}

bean 样例:

需要将原有的属性类增加 Serializable 的继承

@Data
@NoArgsConstructor
public class TokenVO implements Serializable
{
    /**
     * token
     */
    private String token;

    /**
     * token 前缀
     */
    private String tokenHead;
}

编译打包

mvnw clean package  -Dmaven.test.skip=true

混淆效果

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

常见的反编译工具使用 jd-gui。下载地址:http://java-decompiler.github.io/。

直接通过 jd-gui 窗口打开编译打包后的 jar 文件即可。

混淆常见问题

混淆后自己全部代码没有被放入混淆后的jar包里(jar\BOOT-INF\classes 里面不包含com)

原因: proguard-maven-plugin插件放到了spring-boot-maven-plugin插件后面,应该是因为spring-boot-maven-plugin放在前面会先执行spring-boot-maven-plugin的repackage再执行proguard-maven-plugin的混淆,混淆后没有重新repackage

解决:把proguard-maven-plugin插件放到spring-boot-maven-plugin前面

DataSource,Aop,swagger等相关配置类混淆后导致的运行报错

原因:比如Aop是因为用@Around(value = "apiLog()")指定apiLog方法对应的@Pointcut作为切入点,但是因为apiLog方法被混淆成a导致找不到对应apiLog方法。就不一一列举了

解决:我是直接把所有配置类放到framework下面,然后proguard-maven-plugin配置让framework 下面的类和方法全部不混淆,这样省事点

-keep class cn.fuzhi.task.** {
    *;
}

aop事务控制,事务失效问题

原因:因为aop pointcut 是根据包路径和方法名(update,find)拦截,service 混淆后 包路径和方法名都变成 a,b导致拦截不到

解决:不混淆包路径和方法public方法名,如下

#不混淆service下的public方法名
-keepclassmembers class cn.fuzhi.service.** {public *** *(...);}
#不混淆service包名
-keeppackagenames cn.fuzhi.service.**

swagger 文档看不到api,以及接口请求参数controller 参数绑定报错

1)swagger 文档看不到api:因为我swagger配置的api 是通过.apis(RequestHandlerSelectors.basePackage(packageName))指定了具体包名,controller混淆后包名变成a,b,c所以扫描不到接口自然就生成不了文档

2)api接口请求出错:spring接口参数绑定是根据参数名称,混淆后参数名称会变成var1等,绑定的时候找不到原来定义的参数名就报错了.

解决:proguard-maven-plugin配置所有controller public 方法不参与混淆以及参数也不参与混淆,如下

 
#所有controller类的public方法你参与混淆
<option>-keepclassmembers class cn.fuzhi.controller.* {public *** *(...);}</option>
#不混淆controller的包路径
<option>-keeppackagenames cn.fuzhi.controller</option>
 
#参数不参与混淆
<option>-keepparameternames</option>

spring controller 参数绑定问题

原因:spring参数绑定有些是根据controller的方法参数名称绑定的,参数名称混淆后就会绑定不上

解决:

方法1:Controller用如@RequestParam的方式绑定

方法2:

①.配置-keepparameternames,这个参数可以让不被混淆的方法也不会混淆其参数,对interface无效.

<option>-keepparameternames</option>

②. 因为第①步的配置,那只要配置controller里的方法不混淆,那么方法参数也就不会混淆了,如下

-keepclassmembers class cn.fuzhi.controller.* {public *** *(...);}

mybatis 参数绑定报错org.apache.ibatis.binding.BindingException

原因:和第6点类似,因为方法参数被混淆,所以导致mybatis的mapper(是interface)定义的接口参数Id被混淆成 var1,mybatis xml #{id}绑定的时候只能找到var1于是BindingException.但是不能用第6点的方法2,因为keepparameternames 能让keep的class参数不混淆,却不能让interface的参数不参与混淆,而mybatis的mapper就是interface

解决:

方法1:mapper的参数 用 @Param("id") 强行绑定

方法2:此方法也可以解决第6点的问题

①. 配置 -keepattributes MethodParameters ,这个参数可以让不管有没有参与混淆的类的方法参数都不参与混淆,对类和接口都生效,但是这样会导致即使参与混淆的类也无法混淆参数

-keepattributes MethodParameters

②. 上面的配置是可以让所有方法参数不被proguard混淆,但是有些项目在java编译的时候却可能会对方法参数混淆(好像是针对interface方法参数的混淆,不针对class).所以可以配置编译时不混淆方法参数(-parameters),如下所示(这个根据自己项目实际情况配置,好像是和idea版本有关系)

   <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <compilerArgument>-parameters</compilerArgument>
                </configuration>
    </plugin>

CreateProcess error=206, 文件名或扩展名太长。

原因:因为window的cmd有长度限制,而proguard混淆时依赖太多的jar包导致命令行过长

解决:在proguard-maven-plugin的configuration中加入下面配置,这个配置会把jar包放到临时目录以便缩短命令行

<putLibraryJarsInTempDir>true</putLibraryJarsInTempDir>

相关文章:

  • 【嵌入式】C语言中malloc()和calloc()的区别
  • kotlin 内联函数 inline
  • Python编写服务监控程序
  • Unity Shader编程】之透明物体渲染
  • 第五: redis 安装 / find 查找目录
  • JVM常用概念之身份哈希码
  • Apache SeaTunnel脚本升级及参数调优实战
  • 指令系统(2017统考真题)
  • 人工智能在电子信息工程信号处理中的应用调研
  • 离线黑客攻击之绕过BIOS/EFI
  • openstack安装部署
  • docker-存储卷-网络
  • 游戏MOD伴随盗号风险,仿冒网站借“风灵月影”窃密【火绒企业版V2.0】
  • 存算分离是否真的有必要?从架构之争到 Doris 实战解析
  • INT202 Complexity of Algroithms 算法的复杂度 Pt.2 Search Algorithm 搜索算法
  • Error: The resource name must start with a letter
  • 代码随想录第55期训练营第七天|LeetCode454.四数相加II、383.赎金信、15.三数之和、18.四数之和
  • 基于javaweb的SSM+Maven宠物领养宠物商城流浪动物管理系统与实现(源码+文档+部署讲解)
  • 【PCIe 总线及设备入门学习专栏 3.2 -- PCIe 在进行大数据搬运时是如何组包的?】
  • 【STM32实物】基于STM32的太阳能充电宝设计
  • 印巴局势快速升级,外交部:呼吁印巴以和平稳定的大局为重
  • 李云泽:支持设立新的金融资产投资公司,今天即将批复一家
  • 中国首位、亚洲首位!赵心童夺得斯诺克世锦赛冠军
  • 伊朗港口爆炸最新事故调查报告公布,2名管理人员被捕
  • 江南华南较强降雨扰返程,北方大部需防风沙
  • 人民日报和音:引领新时代中俄关系坚毅前行