pom配置
-
<project>
-
<build>
-
<plugins>
-
<plugin>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-maven-plugin</artifactId>
-
</plugin>
-
<!-- 代碼混淆插件 -->
-
<plugin>
-
<groupId>com.github.wvengen</groupId>
-
<artifactId>proguard-maven-plugin</artifactId>
-
<version>2.0.14</version>
-
<executions>
-
<execution>
-
<!-- 混淆時刻,這里是打包的時候混淆 -->
-
<phase>package</phase>
-
<goals>
-
<!-- 使用插件的什么功能,當然是混淆 -->
-
<goal>proguard</goal>
-
</goals>
-
</execution>
-
</executions>
-
<configuration>
-
<!-- 是否將生成的PG文件安裝部署 -->
-
<attach>true</attach>
-
<!-- 是否混淆 -->
-
<obfuscate>true</obfuscate>
-
<!-- 指定生成文件分類 -->
-
<attachArtifactClassifier>pg</attachArtifactClassifier>
-
<!-- ProGuard配置參數(可引用cfg配置文件) -->
-
<options>
-
<!-- JDK目標版本1.8-->
-
<option>-target 1.8</option>
-
<!-- 不做收縮(刪除注釋、未被引用代碼) -->
-
<option>-dontshrink</option>
-
<!-- 不做優化(變更代碼實現邏輯) -->
-
<option>-dontoptimize</option>
-
<!-- 不路過非公用類文件及成員 -->
-
<option>-dontskipnonpubliclibraryclasses</option>
-
<option>-dontskipnonpubliclibraryclassmembers</option>
-
<!--不用大小寫混合類名機制-->
-
<option>-dontusemixedcaseclassnames</option>
-
<!-- 優化時允許訪問並修改有修飾符的類和類的成員 -->
-
<option>-allowaccessmodification</option>
-
<!-- 確定統一的混淆類的成員名稱來增加混淆 -->
-
<option>-useuniqueclassmembernames</option>
-
<!-- 不混淆所有包名,本人測試混淆后WEB項目問題實在太多,畢竟Spring配置中有 大量固定寫法的包名 -->
-
<option>-keeppackagenames</option>
-
<option>-adaptclassstrings</option>
-
<!-- <option>-keepdirectories</option> -->
-
<!-- 不混淆所有特殊的類 -->
-
<option>-keepattributes
-
Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod </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>-keepclassmembers class * {
-
@org.springframework.beans.factory.annotation.Autowired *;
-
@org.springframework.beans.factory.annotation.Value *;
-
}
-
</option>
-
<!-- 混淆這個包下的類 -->
-
<option>-keep class !com.example.** { *; }</option>
-
<!-- 不混淆main方法 -->
-
<option>-keep class com.example.Application { *; }</option>
-
-
<!-- 不混淆所有的set/get方法,畢竟項目中使用的部分第三方框架(例如Shiro)會用到大量的set/get映射 -->
-
<option>-keepclassmembers public class * {void set*(***);*** get*();}</option>
-
<!-- 不混淆包中的所有類以及類的屬性及方法,實體包,混淆了會導致ORM框架及前端無法識別 -->
-
-
<!-- 不對包類的類名進行混淆,但對類中的屬性和方法混淆 -->
-
<option>-keep class com.example.controller.** </option>
-
<option>-keep class com.example.service.** </option>
-
-
<!-- 不混淆包下的所有類名,且類中的方法和屬性也不混淆 -->
-
<option>-keep class com.example.redis.** { *; }</option>
-
<option>-keep class com.example.domain.** { *; }</option>
-
<option>-keep class com.example.dto.** { *; } </option>
-
<option>-keep class com.example.config.**{ *; }</option>
-
<option>-keep class com.example.dao.** { *; }</option>
-
-
</options>
-
<!--class 混淆后輸出的jar包 -->
-
<outjar>${project.build.finalName}-pg.jar</outjar>
-
<!-- 混淆時需要引用的java庫,這些庫的類不會做混淆 -->
-
<libs>
-
<lib>${java.home}/lib/rt.jar</lib>
-
<lib>${java.home}/lib/jce.jar</lib>
-
</libs>
-
<!-- 需要做混淆的jar或目錄 -->
-
<injar>classes</injar>
-
<!-- 輸出目錄 -->
-
<outputDirectory>${project.build.directory}</outputDirectory>
-
-
</configuration>
-
</plugin>
-
-
</plugins>
-
</build>
-
</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命名策略:
-
-
public class ServerApplication {
-
public static void main(String[] args) {
-
new SpringApplicationBuilder(ServerApplication.class)
-
.beanNameGenerator((def,reg)->def.getBeanClassName())
-
.run(args);
-
}
-
}
需要注意的問題:
1、因為有時候會配置不保持包名或類名,因此一些相關配置文件的內容需要改變,好在ProGuard不是隨機生成類名,而是先按照原名稱對相同包下類進行排序,混淆后的類名稱依次為a.class,b.class,c.class.....
那么問題來了,當包中超過26個類時,默認命名為A.class,B.class,C.class,在某些操作系統下,會不區分class文件名稱的大小寫,會導致錯誤(水平所限,未深入探究跟類加載相關),可加入以下配置避免在超過26個類文件時,命名為aa.class,ab.class,ac.class,而不是原來的大寫類名。
-
<!-- 不用大小寫混合類名機制 -->
-
<option>-dontusemixedcaseclassnames</option>
2、打包部署問題。該配置文件打包出來的war中classes文件仍然為正常代碼,需要手動解壓,將classes-pg.jar中classes替換進去,在工程化管理的情況下,可以在jenkins中配置腳本,自動將混淆后的classes替換進war包
3、編譯問題,log4j從2.9版本開始支持JDK9,當項目使用較低版本的proguard對代碼進行混淆時會出現jdk版本兼容錯誤
Can't read [D:\proguardConfig\lib\log4j-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文件的配置,則需要增加如下配置:
-
<!-- 屏蔽log4j中使用java9 api的類 -->
-
<option>-libraryjars lib\log4j-api-2.11.2.jar(!META-INF/versions/9/**.class,!module-info.class)</option>
后來我添加了,還是不行,因為maven 會從我的本地倉庫去獲取jar包,所以這個lib\...jar路徑是錯誤的,需要修改為你本地的maven倉庫地址,同時在<configuration>中添加api的排除:
-
<exclusions>
-
<exclusion>
-
<artifactId>log4j-api</artifactId>
-
<groupId>org.apache.logging.log4j</groupId>
-
</exclusion>
-
</exclusions>
上面這種方式不推薦,后面我去GitHub看了一下將progurad版本改為6.0以上,讓其支持jdk1.8以及更好版本;
在ProGuard<configuration>標簽中增加ProGuard版本配置:
<proguardVersion>6.0.2</proguardVersion>
並且在ProGuard<plugin>標簽中增加依賴:
-
<dependencies>
-
<dependency>
-
<groupId>net.sf.proguard</groupId>
-
<artifactId>proguard-base</artifactId>
-
<version>6.0.2</version>
-
<scope>runtime</scope>
-
</dependency>
-
</dependencies>
然后pom同級目錄添加proguard.cfg文件,其實就是把以前的<options>標簽中的配置放到配置文件中,如下:
-
# JDK目標版本1.8
-
-target 1.8
-
# 不做收縮(刪除注釋、未被引用代碼)
-
-dontshrink
-
# 不做優化(變更代碼實現邏輯)
-
-dontoptimize
-
-ignorewarnings
-
# 不路過非公用類文件及成員
-
-dontskipnonpubliclibraryclasses
-
-dontskipnonpubliclibraryclassmembers
-
<!--不用大小寫混合類名機制
-
-dontusemixedcaseclassnames
-
# 優化時允許訪問並修改有修飾符的類和類的成員
-
-allowaccessmodification
-
# 確定統一的混淆類的成員名稱來增加混淆
-
-useuniqueclassmembernames
-
# 不混淆所有包名,本人測試混淆后WEB項目問題實在太多,畢竟Spring配置中有 大量固定寫法的包名
-
-keeppackagenames
-
-adaptclassstrings
-
# -keepdirectories
-
# 不混淆所有特殊的類
-
-keepattributes
-
Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
-
# This option will save all original methods parameters in files defined in -keep sections, otherwise all parameter names will be obfuscate.
-
-keepparameternames
-
-keep interface * extends * { *; }
-
-keepclassmembers class * {
-
@org.springframework.beans.factory.annotation.Autowired *;
-
@org.springframework.beans.factory.annotation.Value *;
-
}
-
-
# 混淆這個包下的類
-
-keep class !com.example.** { *; }
-
# 不混淆main方法
-
-keep class com.example.Application { *; }
-
-
# 不混淆所有的set/get方法,畢竟項目中使用的部分第三方框架(例如Shiro)會用到大量的set/get映射
-
-keepclassmembers public class * {void set*(***);*** get*();}
-
-
# 不對包類的類名進行混淆,但對類中的屬性和方法混淆
-
-keep class com.example.controller.**
-
-keep class com.example.service.**
-
-keep class com.example.repository.**
-
-
# 不混淆包下的所有類名,且類中的方法和屬性也不混淆
-
-keep class com.example.redis.** { *; }
-
-keep class com.example.domain.** { *; }
-
-keep class com.example.dto.** { *; }
-
-keep class com.example.config.**{ *; }
-
-keep class com.example.dao.** { *; }