• [springboot] mvn编译实现代码混淆


    pom配置

    1.  
      <project>
    2.  
      <build>
    3.  
      <plugins>
    4.  
      <plugin>
    5.  
      <groupId>org.springframework.boot</groupId>
    6.  
      <artifactId>spring-boot-maven-plugin</artifactId>
    7.  
      </plugin>
    8.  
      <!-- 代码混淆插件 -->
    9.  
      <plugin>
    10.  
      <groupId>com.github.wvengen</groupId>
    11.  
      <artifactId>proguard-maven-plugin</artifactId>
    12.  
      <version>2.0.14</version>
    13.  
      <executions>
    14.  
      <execution>
    15.  
      <!-- 混淆时刻,这里是打包的时候混淆 -->
    16.  
      <phase>package</phase>
    17.  
      <goals>
    18.  
      <!-- 使用插件的什么功能,当然是混淆 -->
    19.  
      <goal>proguard</goal>
    20.  
      </goals>
    21.  
      </execution>
    22.  
      </executions>
    23.  
      <configuration>
    24.  
      <!-- 是否将生成的PG文件安装部署 -->
    25.  
      <attach>true</attach>
    26.  
      <!-- 是否混淆 -->
    27.  
      <obfuscate>true</obfuscate>
    28.  
      <!-- 指定生成文件分类 -->
    29.  
      <attachArtifactClassifier>pg</attachArtifactClassifier>
    30.  
      <!-- ProGuard配置参数(可引用cfg配置文件) -->
    31.  
      <options>
    32.  
      <!-- JDK目标版本1.8-->
    33.  
      <option>-target 1.8</option>
    34.  
      <!-- 不做收缩(删除注释、未被引用代码) -->
    35.  
      <option>-dontshrink</option>
    36.  
      <!-- 不做优化(变更代码实现逻辑) -->
    37.  
      <option>-dontoptimize</option>
    38.  
      <!-- 不路过非公用类文件及成员 -->
    39.  
      <option>-dontskipnonpubliclibraryclasses</option>
    40.  
      <option>-dontskipnonpubliclibraryclassmembers</option>
    41.  
      <!--不用大小写混合类名机制-->
    42.  
      <option>-dontusemixedcaseclassnames</option>
    43.  
      <!-- 优化时允许访问并修改有修饰符的类和类的成员 -->
    44.  
      <option>-allowaccessmodification</option>
    45.  
      <!-- 确定统一的混淆类的成员名称来增加混淆 -->
    46.  
      <option>-useuniqueclassmembernames</option>
    47.  
      <!-- 不混淆所有包名,本人测试混淆后WEB项目问题实在太多,毕竟Spring配置中有 大量固定写法的包名 -->
    48.  
      <option>-keeppackagenames</option>
    49.  
      <option>-adaptclassstrings</option>
    50.  
      <!-- <option>-keepdirectories</option> -->
    51.  
      <!-- 不混淆所有特殊的类 -->
    52.  
      <option>-keepattributes
    53.  
      Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod</option>
    54.  
      <!-- This option will save all original methods parameters in files defined
    55.  
      in -keep sections, otherwise all parameter names will be obfuscate. -->
    56.  
      <option>-keepparameternames</option>
    57.  
      <option>-keepclassmembers class * {
    58.  
      @org.springframework.beans.factory.annotation.Autowired *;
    59.  
      @org.springframework.beans.factory.annotation.Value *;
    60.  
      }
    61.  
      </option>
    62.  
      <!-- 混淆这个包下的类 -->
    63.  
      <option>-keep class !com.example.** { *; }</option>
    64.  
      <!-- 不混淆main方法 -->
    65.  
      <option>-keep class com.example.Application { *; }</option>
    66.  
       
    67.  
      <!-- 不混淆所有的set/get方法,毕竟项目中使用的部分第三方框架(例如Shiro)会用到大量的set/get映射 -->
    68.  
      <option>-keepclassmembers public class * {void set*(***);*** get*();}</option>
    69.  
      <!-- 不混淆包中的所有类以及类的属性及方法,实体包,混淆了会导致ORM框架及前端无法识别 -->
    70.  
       
    71.  
      <!-- 不对包类的类名进行混淆,但对类中的属性和方法混淆 -->
    72.  
      <option>-keep class com.example.controller.** </option>
    73.  
      <option>-keep class com.example.service.** </option>
    74.  
       
    75.  
      <!-- 不混淆包下的所有类名,且类中的方法和属性也不混淆 -->
    76.  
      <option>-keep class com.example.redis.** { *; }</option>
    77.  
      <option>-keep class com.example.domain.** { *; }</option>
    78.  
      <option>-keep class com.example.dto.** { *; } </option>
    79.  
      <option>-keep class com.example.config.**{ *; }</option>
    80.  
      <option>-keep class com.example.dao.** { *; }</option>
    81.  
       
    82.  
      </options>
    83.  
      <!--class 混淆后输出的jar包 -->
    84.  
      <outjar>${project.build.finalName}-pg.jar</outjar>
    85.  
      <!-- 混淆时需要引用的java库,这些库的类不会做混淆 -->
    86.  
      <libs>
    87.  
      <lib>${java.home}/lib/rt.jar</lib>
    88.  
      <lib>${java.home}/lib/jce.jar</lib>
    89.  
      </libs>
    90.  
      <!-- 需要做混淆的jar或目录 -->
    91.  
      <injar>classes</injar>
    92.  
      <!-- 输出目录 -->
    93.  
      <outputDirectory>${project.build.directory}</outputDirectory>
    94.  
       
    95.  
      </configuration>
    96.  
      </plugin>
    97.  
       
    98.  
      </plugins>
    99.  
      </build>
    100.  
      </project>

          使用时将上面pom文件中的plugin标签中的内容引入到自己的pom文件中,将具体的包名换成自己的即可。具体的配置注释中都有,使用过程中主要难点是每个包可混淆到哪种程度,具体的情况需要时最好可以自行进行实验进行验证。下面我将我的混淆配置讲解一下:

    • <option>-keep class !com.example.** { *; }</option> 是混淆这个包下的类
    • 对于controller、service以及定时任务这三个包,我采用的混淆程度为不对类名进行混淆,但是对属性和方法进行混淆
    • controller层注意在使用@PathVariable@RequestParam时需要显式声明参数名
    • 对于dao、pojo、dto以及配置类的包,我不进行混淆

           以上的配置是我自己经过一次次尝试发现的所能混淆的最大程度,否则会报错。注意,在proguard-maven-plugin中默认是对类名,属性,方法全部混淆,所以如果全部混淆,则不需要配置这个包即可。我个人的感悟是混淆的程度主要是由于很多类是交由spring管理,所以这些spring bean的类名以及所注入的对象(由spring管理)最好不要混淆类名,否则spring无法识别以及管理,另外就是DAO类,如果是MySQL这种通过Mapper的XML文件映射的类型,最好不要混淆,否则DAO无法找到对应的SQL语句,其他例如Hbase等通过代码查询,读者可尝试进行混淆。一些工具包也是可以进行混淆的,混淆到那种程度读者可进行尝试。

           完成上述配置后,便可以执行maven命令进行打包,最终在target目录下会生成自己项目的jar包以及一个classes-pg.jar的jar包,其中自己项目的jar包是没有混淆代码的jar包,而classes-pg.jar包中的内容才是真正混淆后的代码,将项目的jar包用winrar打开(切记不饿能解压,否则jar包无法运行),进入到classes目录下,将classes-pg.jar中的内容(不包括META-INF文件夹)复制到项目的jar包的classes目录下即可,这样最后的项目的jar包便是进行代码混淆后的jar包,读者可自行反编译观察一下代码混淆的效果,如果启动后报错,读者可根据错误信息修改混淆的类。如果报错的内容是类名冲突之类的,可以尝试在启动类中修改bean命名策略:

    1.  
      @SpringBootApplication
    2.  
      public class ServerApplication {
    3.  
      public static void main(String[] args) {
    4.  
      new SpringApplicationBuilder(ServerApplication.class)
    5.  
      .beanNameGenerator((def,reg)->def.getBeanClassName())
    6.  
      .run(args);
    7.  
      }
    8.  
      }

    需要注意的问题:

      1、因为有时候会配置不保持包名或类名,因此一些相关配置文件的内容需要改变,好在ProGuard不是随机生成类名,而是先按照原名称对相同包下类进行排序,混淆后的类名称依次为a.class,b.class,c.class.....

    那么问题来了,当包中超过26个类时,默认命名为A.class,B.class,C.class,在某些操作系统下,会不区分class文件名称的大小写,会导致错误(水平所限,未深入探究跟类加载相关),可加入以下配置避免在超过26个类文件时,命名为aa.class,ab.class,ac.class,而不是原来的大写类名。

    1.  
      <!-- 不用大小写混合类名机制 -->
    2.  
      <option>-dontusemixedcaseclassnames</option>

      2、打包部署问题。该配置文件打包出来的war中classes文件仍然为正常代码,需要手动解压,将classes-pg.jar中classes替换进去,在工程化管理的情况下,可以在jenkins中配置脚本,自动将混淆后的classes替换进war包

      3、编译问题,log4j从2.9版本开始支持JDK9,当项目使用较低版本的proguard对代码进行混淆时会出现jdk版本兼容错误

    Can't read [D:proguardConfigliblog4j-api-2.10.0.jar] (Can't process class [META-INF/versions/9/org/apache/logging/log4j/util/ProcessIdUtil.class] (Unsupported class version number [53.0] (maximum 52.0, Java 1.8)))
    

    要解决报错需屏蔽log4j中使用java9 api的类。在proguard的配置文件中log4j-api-2.10.0.jar后面加入(!META-INF/versions/9/**.class,!module-info.class)。如果是pom文件的配置,则需要增加如下配置:

    1.  
      <!-- 屏蔽log4j中使用java9 api的类 -->
    2.  
      <option>-libraryjars liblog4j-api-2.11.2.jar(!META-INF/versions/9/**.class,!module-info.class)</option>

    后来我添加了,还是不行,因为maven 会从我的本地仓库去获取jar包,所以这个lib...jar路径是错误的,需要修改为你本地的maven仓库地址,同时在<configuration>中添加api的排除:

    1.  
      <exclusions>
    2.  
      <exclusion>
    3.  
      <artifactId>log4j-api</artifactId>
    4.  
      <groupId>org.apache.logging.log4j</groupId>
    5.  
      </exclusion>
    6.  
      </exclusions>

    上面这种方式不推荐,后面我去GitHub看了一下将progurad版本改为6.0以上,让其支持jdk1.8以及更好版本;

    在ProGuard<configuration>标签中增加ProGuard版本配置:

    <proguardVersion>6.0.2</proguardVersion>

    并且在ProGuard<plugin>标签中增加依赖:

    1.  
      <dependencies>
    2.  
      <dependency>
    3.  
      <groupId>net.sf.proguard</groupId>
    4.  
      <artifactId>proguard-base</artifactId>
    5.  
      <version>6.0.2</version>
    6.  
      <scope>runtime</scope>
    7.  
      </dependency>
    8.  
      </dependencies>

    然后pom同级目录添加proguard.cfg文件,其实就是把以前的<options>标签中的配置放到配置文件中,如下:

    1.  
      # JDK目标版本1.8
    2.  
      -target 1.8
    3.  
      # 不做收缩(删除注释、未被引用代码)
    4.  
      -dontshrink
    5.  
      # 不做优化(变更代码实现逻辑)
    6.  
      -dontoptimize
    7.  
      -ignorewarnings
    8.  
      # 不路过非公用类文件及成员
    9.  
      -dontskipnonpubliclibraryclasses
    10.  
      -dontskipnonpubliclibraryclassmembers
    11.  
      <!--不用大小写混合类名机制
    12.  
      -dontusemixedcaseclassnames
    13.  
      # 优化时允许访问并修改有修饰符的类和类的成员
    14.  
      -allowaccessmodification
    15.  
      # 确定统一的混淆类的成员名称来增加混淆
    16.  
      -useuniqueclassmembernames
    17.  
      # 不混淆所有包名,本人测试混淆后WEB项目问题实在太多,毕竟Spring配置中有 大量固定写法的包名
    18.  
      -keeppackagenames
    19.  
      -adaptclassstrings
    20.  
      # -keepdirectories
    21.  
      # 不混淆所有特殊的类
    22.  
      -keepattributes
    23.  
      Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
    24.  
      # This option will save all original methods parameters in files defined in -keep sections, otherwise all parameter names will be obfuscate.
    25.  
      -keepparameternames
    26.  
      -keep interface * extends * { *; }
    27.  
      -keepclassmembers class * {
    28.  
      @org.springframework.beans.factory.annotation.Autowired *;
    29.  
      @org.springframework.beans.factory.annotation.Value *;
    30.  
      }
    31.  
       
    32.  
      # 混淆这个包下的类
    33.  
      -keep class !com.example.** { *; }
    34.  
      # 不混淆main方法
    35.  
      -keep class com.example.Application { *; }
    36.  
       
    37.  
      # 不混淆所有的set/get方法,毕竟项目中使用的部分第三方框架(例如Shiro)会用到大量的set/get映射
    38.  
      -keepclassmembers public class * {void set*(***);*** get*();}
    39.  
       
    40.  
      # 不对包类的类名进行混淆,但对类中的属性和方法混淆
    41.  
      -keep class com.example.controller.**
    42.  
      -keep class com.example.service.**
    43.  
      -keep class com.example.repository.**
    44.  
       
    45.  
      # 不混淆包下的所有类名,且类中的方法和属性也不混淆
    46.  
      -keep class com.example.redis.** { *; }
    47.  
      -keep class com.example.domain.** { *; }
    48.  
      -keep class com.example.dto.** { *; }
    49.  
      -keep class com.example.config.**{ *; }
    50.  
      -keep class com.example.dao.** { *; }

    参考资料

  • 相关阅读:
    win10安装nodejs,修改全局依赖位置和环境变量配置
    JavaScript判断两个对象内容是否相等
    JS判断是否是数组
    Js判断值是否是NaN
    typeof方法重写(区分数组对象)
    JS实现图片懒加载
    输入url到展示页面过程发生了什么?
    html如何在服务端跑起来
    nuxt怎么打包
    如果scss引用了字体图标文件该怎么打包
  • 原文地址:https://www.cnblogs.com/exmyth/p/15357976.html
Copyright © 2020-2023  润新知