JustryDeng 4 лет назад
Родитель
Сommit
50dfa7210c
37 измененных файлов с 713 добавлено и 114 удалено
  1. 75 11
      README.en.md
  2. 75 11
      README.md
  3. 1 1
      class-winter-core/dependency-reduced-pom.xml
  4. 1 1
      class-winter-core/pom.xml
  5. 9 0
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/Forward.java
  6. 52 15
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/Reverses.java
  7. 54 4
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/DecryptExecutor.java
  8. 209 20
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/EncryptExecutor.java
  9. 34 8
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/Constant.java
  10. 61 3
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/JarUtil.java
  11. 3 8
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/JavassistUtil.java
  12. 44 3
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/AllTest.java
  13. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/AlreadyProtectedLibs_Test.java
  14. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Debug_Test.java
  15. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/ExcludePrefix_Test.java
  16. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/FinalName_Test.java
  17. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/IncludeLibs_Test.java
  18. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/IncludePrefix_Test.java
  19. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/OriginJarOrWar_Test.java
  20. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Password_Test.java
  21. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/SupportFile_Test.java
  22. 3 3
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Tips_Test.java
  23. 10 10
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/War_Test.java
  24. 51 0
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Xml_Test.java
  25. BIN
      class-winter-core/src/test/resources/class-winter-core-1.1.0.jar
  26. BIN
      class-winter-core/src/test/resources/class-winter-core-1.2.0.jar
  27. BIN
      class-winter-core/src/test/resources/class-winter-core-1.2.1.jar
  28. BIN
      class-winter-core/src/test/resources/class-winter-core-1.2.2.jar
  29. BIN
      class-winter-core/src/test/resources/class-winter-core-2.1.0.jar
  30. BIN
      class-winter-core/src/test/resources/my-project-with-encrypted-lib-have-pwd.jar
  31. BIN
      class-winter-core/src/test/resources/my-project-with-encrypted-lib-have-pwd.war
  32. BIN
      class-winter-core/src/test/resources/my-project-with-encrypted-lib-no-pwd.jar
  33. BIN
      class-winter-core/src/test/resources/my-project-with-encrypted-lib-no-pwd.zip
  34. BIN
      class-winter-core/src/test/resources/xml-test-project.jar
  35. 1 1
      class-winter-maven-plugin/pom.xml
  36. 15 0
      class-winter-maven-plugin/src/main/java/winter/com/ideaaedi/classwinter/plugin/ClassWinterPlugin.java
  37. 1 1
      pom.xml

+ 75 - 11
README.en.md

@@ -1,12 +1,27 @@
-### <font face="幼圆"  color = "#F53B45">**郑重声明**</font>
+# 代码混淆之class-winter
 
-&emsp;&emsp;class-winter是本人在学习完[**class-final(v1.1.9)**](https://gitee.com/roseboy/classfinal) 后,仿照class-final进行编写的,整体思路与class-final一致,代码部分(约20%~30%)复用了class-final中的代码。可将class-winter看作是class-final的一个分支。
+- [代码混淆之class-winter](#代码混淆之class-winter)
+  - [<font  face="幼圆" color = "#3399EA" >功能与特性</font>](#font--face幼圆-color--3399ea-功能与特性font)
+  - [<font  face="幼圆" color = "#3399EA" >加密</font>](#font--face幼圆-color--3399ea-加密font)
+  - [<font  face="幼圆" color = "#3399EA" >加密参数</font>](#font--face幼圆-color--3399ea-加密参数font)
+  - [<font  face="幼圆" color = "#3399EA" >解密(启动)</font>](#font--face幼圆-color--3399ea-解密启动font)
+  - [<font  face="幼圆" color = "#3399EA" >解密参数</font>](#font--face幼圆-color--3399ea-解密参数font)
+  - [<font  face="幼圆" color = "#3399EA" >相关资料</font>](#font--face幼圆-color--3399ea-相关资料font)
+
+---
+
+<font face="幼圆"  color = "#F53B45">**郑重声明**</font>
+
+&emsp;&emsp;[**class-winter**](https://gitee.com/JustryDeng/class-winter) 是本人在学习完 [**class-final(v1.1.9)**](https://gitee.com/roseboy/classfinal) 后,仿照class-final进行编写的,部分思路与class-final一致。
+
+## <font  face="幼圆" color = "#3399EA" >功能与特性</font>
 
-### <font  face="幼圆" color = "#3399EA" >功能与特性</font>
 - 支持war加密。
-- 支持jar(普通jar+可执行jar)加密。
+- 支持jar(普通jar+可执行jar)加密。
+- 支持xml加密(掩耳盗铃版)。
+
+## <font  face="幼圆" color = "#3399EA" >加密</font>
 
-### <font  face="幼圆" color = "#3399EA" >加密</font>
 - #### <font face="幼圆" ><font face="幼圆"  color = "#86CA5E">**方式一**</font>:通过maven插件自动加密。</font>
   ```xml
   <!--
@@ -23,6 +38,9 @@
           <!-- <finalName></finalName>-->
           <includePrefix>加密范围</includePrefix>
           <!-- <excludePrefix></excludePrefix>-->
+          <!-- <includeXmlPrefix></includeXmlPrefix>-->
+          <!-- <excludePrefix></excludePrefix>-->
+          <!-- <toCleanXmlChildElementName></toCleanXmlChildElementName>-->
           <!-- <password></password>-->
           <!-- <includeLibs></includeLibs>-->
           <!-- <alreadyProtectedLibs></alreadyProtectedLibs>-->
@@ -42,7 +60,7 @@
   ```
   注:不必担心信息泄漏问题,使用此方式生成混淆的jar包时,会擦除pom.xml中关于class-winter-plugin的信息。
 
-- #### <font face="幼圆" ><font face="幼圆"  color = "#86CA5E">**方式二**</font>:通过[**class-winter-core.jar**](https://gitee.com/JustryDeng/class-winter/raw/master/class-winter-core/src/test/resources/class-winter-core-2.0.0.jar) 主动加密。</font>
+- #### <font face="幼圆" ><font face="幼圆"  color = "#86CA5E">**方式二**</font>:通过 [**class-winter-core.jar**](https://gitee.com/JustryDeng/class-winter/raw/master/class-winter-core/src/test/resources/class-winter-core-2.1.0.jar) 主动加密。</font>
 
   ```bash
   java -jar class-winter-core-${version}.jar originJarOrWar=${要加密的项目.jar或.war包} includePrefix=${加密范围} [k3=v3 k4=v4 ...]
@@ -53,7 +71,8 @@
   java -jar class-winter-core-${version}.jar k1="v1" k2="v2"
   ```
 
-### <font  face="幼圆" color = "#3399EA" >加密参数</font>
+## <font  face="幼圆" color = "#3399EA" >加密参数</font>
+
 <table>
   <tr align="center">
     <td>参数</td>
@@ -86,6 +105,41 @@ includePrefix=com,org</td>
 <br/>
 注:excludePrefix优先级高于includePrefix。</td>
     <td>excludePrefix=com.example.service,com.example.util.StrUtil.class</td>
+  </tr>
+      </tr>
+    <tr align="center">
+    <td>includeXmlPrefix</td>
+    <td>否</td>
+    <td>通过打出来的包中条目的entryName前缀匹配的形式定位要加密的xml
+    <br/>
+注:多个通过逗号分割。    
+    <br/>
+注:如果您打出来的加密包是准备作为一个lib包提供给第三方使用的,那么请不要使用此参数,因为解密时是不会解密项目所依赖的lib包中的xml的。</td>
+    <td>includeXmlPrefix=BOOT-INF/classes/
+    <br/>
+includeXmlPrefix=BOOT-INF/classes/com/demo/mapper/,BOOT-INF/classes/com/demo/dao/</td>
+  </tr>
+  </tr>
+    <tr align="center">
+    <td>excludeXmlPrefix</td>
+    <td>否</td>
+    <td>通过打出来的包中条目的entryName前缀匹配的形式排除xml,不对其加密
+    <br/>
+注:多个通过逗号分割。</td>
+    <td>excludeXmlPrefix=BOOT-INF/classes/com/demo/mapper/
+    <br/>
+excludeXmlPrefix=BOOT-INF/classes/com/demo/mapper/,BOOT-INF/classes/com/demo/dao/UserDao.xml</td>
+  </tr>
+  </tr>
+    <tr align="center">
+    <td>toCleanXmlChildElementName</td>
+    <td>否</td>
+    <td>加密xml中的哪些一级元素
+    <br/>
+注:默认值为resultMap,sql,insert,update,delete,select
+    <br/>
+注:多个通过逗号分割。</td>
+    <td>toCleanXmlChildElementName=select,delete,resultMap</td>
   </tr>
     <tr align="center">
     <td>finalName</td>
@@ -129,7 +183,7 @@ includePrefix=com,org</td>
     <br/>
 alreadyProtectedLibs=a.jar,b.jar:pwd123
 <br/>
-alreadyProtectedLibs=a.jar:pwd1,b.jar:2</td>
+alreadyProtectedLibs=a.jar:pwd1,b.jar:pwd2</td>
   </tr>
     <tr align="center">
     <td>supportFile</td>
@@ -162,7 +216,10 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
 </table>
 
 
-### <font  face="幼圆" color = "#3399EA" >解密(启动)</font>
+
+
+## <font  face="幼圆" color = "#3399EA" >解密(启动)</font>
+
 通过-javaagent指定代理进行解密启动。
 - #### <font  face="幼圆" color = "#86CA5E" >jar解密(启动)</font>
   ```bash
@@ -218,7 +275,8 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
     set JAVA_OPTS="-javaagent:D:/class-winter-core-1.0.0.jar"
     ```
 
-### <font  face="幼圆" color = "#3399EA" >解密参数</font>
+## <font  face="幼圆" color = "#3399EA" >解密参数</font>
+
 <table>
   <tr align="center">
     <td>参数</td>
@@ -238,4 +296,10 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
     <td>是否开启debug模式</td>
     <td>debug=true</td>
   </tr>
-</table>
+</table>
+
+---
+
+## <font  face="幼圆" color = "#3399EA" >相关资料</font>
+
+- [官网](https://gitee.com/JustryDeng/class-winter)

+ 75 - 11
README.md

@@ -1,12 +1,27 @@
-### <font face="幼圆"  color = "#F53B45">**郑重声明**</font>
+# 代码混淆之class-winter
 
-&emsp;&emsp;class-winter是本人在学习完[**class-final(v1.1.9)**](https://gitee.com/roseboy/classfinal) 后,仿照class-final进行编写的,整体思路与class-final一致,代码部分(约20%~30%)复用了class-final中的代码。可将class-winter看作是class-final的一个分支。
+- [代码混淆之class-winter](#代码混淆之class-winter)
+  - [<font  face="幼圆" color = "#3399EA" >功能与特性</font>](#font--face幼圆-color--3399ea-功能与特性font)
+  - [<font  face="幼圆" color = "#3399EA" >加密</font>](#font--face幼圆-color--3399ea-加密font)
+  - [<font  face="幼圆" color = "#3399EA" >加密参数</font>](#font--face幼圆-color--3399ea-加密参数font)
+  - [<font  face="幼圆" color = "#3399EA" >解密(启动)</font>](#font--face幼圆-color--3399ea-解密启动font)
+  - [<font  face="幼圆" color = "#3399EA" >解密参数</font>](#font--face幼圆-color--3399ea-解密参数font)
+  - [<font  face="幼圆" color = "#3399EA" >相关资料</font>](#font--face幼圆-color--3399ea-相关资料font)
+
+---
+
+<font face="幼圆"  color = "#F53B45">**郑重声明**</font>
+
+&emsp;&emsp;[**class-winter**](https://gitee.com/JustryDeng/class-winter) 是本人在学习完 [**class-final(v1.1.9)**](https://gitee.com/roseboy/classfinal) 后,仿照class-final进行编写的,部分思路与class-final一致。
+
+## <font  face="幼圆" color = "#3399EA" >功能与特性</font>
 
-### <font  face="幼圆" color = "#3399EA" >功能与特性</font>
 - 支持war加密。
-- 支持jar(普通jar+可执行jar)加密。
+- 支持jar(普通jar+可执行jar)加密。
+- 支持xml加密(掩耳盗铃版)。
+
+## <font  face="幼圆" color = "#3399EA" >加密</font>
 
-### <font  face="幼圆" color = "#3399EA" >加密</font>
 - #### <font face="幼圆" ><font face="幼圆"  color = "#86CA5E">**方式一**</font>:通过maven插件自动加密。</font>
   ```xml
   <!--
@@ -23,6 +38,9 @@
           <!-- <finalName></finalName>-->
           <includePrefix>加密范围</includePrefix>
           <!-- <excludePrefix></excludePrefix>-->
+          <!-- <includeXmlPrefix></includeXmlPrefix>-->
+          <!-- <excludePrefix></excludePrefix>-->
+          <!-- <toCleanXmlChildElementName></toCleanXmlChildElementName>-->
           <!-- <password></password>-->
           <!-- <includeLibs></includeLibs>-->
           <!-- <alreadyProtectedLibs></alreadyProtectedLibs>-->
@@ -42,7 +60,7 @@
   ```
   注:不必担心信息泄漏问题,使用此方式生成混淆的jar包时,会擦除pom.xml中关于class-winter-plugin的信息。
 
-- #### <font face="幼圆" ><font face="幼圆"  color = "#86CA5E">**方式二**</font>:通过[**class-winter-core.jar**](https://gitee.com/JustryDeng/class-winter/raw/master/class-winter-core/src/test/resources/class-winter-core-2.0.0.jar) 主动加密。</font>
+- #### <font face="幼圆" ><font face="幼圆"  color = "#86CA5E">**方式二**</font>:通过 [**class-winter-core.jar**](https://gitee.com/JustryDeng/class-winter/raw/master/class-winter-core/src/test/resources/class-winter-core-2.1.0.jar) 主动加密。</font>
 
   ```bash
   java -jar class-winter-core-${version}.jar originJarOrWar=${要加密的项目.jar或.war包} includePrefix=${加密范围} [k3=v3 k4=v4 ...]
@@ -53,7 +71,8 @@
   java -jar class-winter-core-${version}.jar k1="v1" k2="v2"
   ```
 
-### <font  face="幼圆" color = "#3399EA" >加密参数</font>
+## <font  face="幼圆" color = "#3399EA" >加密参数</font>
+
 <table>
   <tr align="center">
     <td>参数</td>
@@ -86,6 +105,41 @@ includePrefix=com,org</td>
 <br/>
 注:excludePrefix优先级高于includePrefix。</td>
     <td>excludePrefix=com.example.service,com.example.util.StrUtil.class</td>
+  </tr>
+      </tr>
+    <tr align="center">
+    <td>includeXmlPrefix</td>
+    <td>否</td>
+    <td>通过打出来的包中条目的entryName前缀匹配的形式定位要加密的xml
+    <br/>
+注:多个通过逗号分割。    
+    <br/>
+注:如果您打出来的加密包是准备作为一个lib包提供给第三方使用的,那么请不要使用此参数,因为解密时是不会解密项目所依赖的lib包中的xml的。</td>
+    <td>includeXmlPrefix=BOOT-INF/classes/
+    <br/>
+includeXmlPrefix=BOOT-INF/classes/com/demo/mapper/,BOOT-INF/classes/com/demo/dao/</td>
+  </tr>
+  </tr>
+    <tr align="center">
+    <td>excludeXmlPrefix</td>
+    <td>否</td>
+    <td>通过打出来的包中条目的entryName前缀匹配的形式排除xml,不对其加密
+    <br/>
+注:多个通过逗号分割。</td>
+    <td>excludeXmlPrefix=BOOT-INF/classes/com/demo/mapper/
+    <br/>
+excludeXmlPrefix=BOOT-INF/classes/com/demo/mapper/,BOOT-INF/classes/com/demo/dao/UserDao.xml</td>
+  </tr>
+  </tr>
+    <tr align="center">
+    <td>toCleanXmlChildElementName</td>
+    <td>否</td>
+    <td>加密xml中的哪些一级元素
+    <br/>
+注:默认值为resultMap,sql,insert,update,delete,select
+    <br/>
+注:多个通过逗号分割。</td>
+    <td>toCleanXmlChildElementName=select,delete,resultMap</td>
   </tr>
     <tr align="center">
     <td>finalName</td>
@@ -129,7 +183,7 @@ includePrefix=com,org</td>
     <br/>
 alreadyProtectedLibs=a.jar,b.jar:pwd123
 <br/>
-alreadyProtectedLibs=a.jar:pwd1,b.jar:2</td>
+alreadyProtectedLibs=a.jar:pwd1,b.jar:pwd2</td>
   </tr>
     <tr align="center">
     <td>supportFile</td>
@@ -162,7 +216,10 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
 </table>
 
 
-### <font  face="幼圆" color = "#3399EA" >解密(启动)</font>
+
+
+## <font  face="幼圆" color = "#3399EA" >解密(启动)</font>
+
 通过-javaagent指定代理进行解密启动。
 - #### <font  face="幼圆" color = "#86CA5E" >jar解密(启动)</font>
   ```bash
@@ -218,7 +275,8 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
     set JAVA_OPTS="-javaagent:D:/class-winter-core-1.0.0.jar"
     ```
 
-### <font  face="幼圆" color = "#3399EA" >解密参数</font>
+## <font  face="幼圆" color = "#3399EA" >解密参数</font>
+
 <table>
   <tr align="center">
     <td>参数</td>
@@ -238,4 +296,10 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
     <td>是否开启debug模式</td>
     <td>debug=true</td>
   </tr>
-</table>
+</table>
+
+---
+
+## <font  face="幼圆" color = "#3399EA" >相关资料</font>
+
+- [官网](https://gitee.com/JustryDeng/class-winter)

+ 1 - 1
class-winter-core/dependency-reduced-pom.xml

@@ -3,7 +3,7 @@
   <parent>
     <artifactId>class-winter</artifactId>
     <groupId>com.idea-aedi</groupId>
-    <version>2.0.0</version>
+    <version>2.1.0</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>class-winter-core</artifactId>

+ 1 - 1
class-winter-core/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.idea-aedi</groupId>
         <artifactId>class-winter</artifactId>
-        <version>2.0.0</version>
+        <version>2.1.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 9 - 0
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/Forward.java

@@ -26,6 +26,9 @@ public class Forward {
         String originJarOrWar = parseValueByPrefixFromTail("originJarOrWar=", args);
         String includePrefix = parseValueByPrefixFromTail("includePrefix=", args);
         String excludePrefix = parseValueByPrefixFromTail("excludePrefix=", args);
+        String includeXmlPrefix = parseValueByPrefixFromTail("includeXmlPrefix=", args);
+        String excludeXmlPrefix = parseValueByPrefixFromTail("excludeXmlPrefix=", args);
+        String toCleanXmlChildElementName = parseValueByPrefixFromTail("toCleanXmlChildElementName=", args);
         String finalName = parseValueByPrefixFromTail("finalName=", args);
         String password = parseValueByPrefixFromTail("password=", args);
         String includeLibs = parseValueByPrefixFromTail("includeLibs=", args);
@@ -38,6 +41,9 @@ public class Forward {
         Logger.debug(Forward.class, "You input arg originJarOrWar -> " + originJarOrWar);
         Logger.debug(Forward.class, "You input arg includePrefix -> " + includePrefix);
         Logger.debug(Forward.class, "You input arg excludePrefix -> " + excludePrefix);
+        Logger.debug(Forward.class, "You input arg includeXmlPrefix -> " + includeXmlPrefix);
+        Logger.debug(Forward.class, "You input arg excludeXmlPrefix -> " + excludeXmlPrefix);
+        Logger.debug(Forward.class, "You input arg toCleanXmlChildElementName -> " + toCleanXmlChildElementName);
         Logger.debug(Forward.class, "You input arg finalName -> " + finalName);
         Logger.debug(Forward.class, "You input arg password -> " + password);
         Logger.debug(Forward.class, "You input arg includeLibs -> " + includeLibs);
@@ -51,6 +57,9 @@ public class Forward {
                 .originJarOrWar(originJarOrWar)
                 .includePrefix(includePrefix)
                 .excludePrefix(excludePrefix)
+                .includeXmlPrefix(includeXmlPrefix)
+                .excludeXmlPrefix(excludeXmlPrefix)
+                .toCleanXmlChildElementName(toCleanXmlChildElementName)
                 .finalName(finalName)
                 .password(password)
                 .includeLibs(includeLibs)

+ 52 - 15
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/Reverses.java

@@ -9,13 +9,19 @@ import winter.com.ideaaedi.classwinter.util.IOUtil;
 import winter.com.ideaaedi.classwinter.util.JarUtil;
 import winter.com.ideaaedi.classwinter.util.JavaagentCmdArgs;
 import winter.com.ideaaedi.classwinter.util.Logger;
+import winter.com.ideaaedi.classwinter.util.Pair;
 import winter.com.ideaaedi.classwinter.util.StrUtil;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.Instrumentation;
 import java.nio.charset.StandardCharsets;
 import java.security.ProtectionDomain;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.zip.ZipFile;
 
 /**
  * 反向解密(Agent类)
@@ -42,8 +48,10 @@ public class Reverses {
             Logger.ENABLE_DEBUG.set(javaagentCmdArgs.isDebug());
         }
         
-        Logger.debug(Reverses.class, "parse raw args [" + args + "] to javaagentCmdArgs -> " + javaagentCmdArgs);
-        
+        Logger.debug(Reverses.class, "Parse raw args [" + args + "] to javaagentCmdArgs -> " + javaagentCmdArgs);
+    
+        final AtomicBoolean firstExec = new AtomicBoolean(false);
+    
         // 在JVM加载class字节码之前,通过ClassFileTransformer修改字节码
         if (instrumentation != null) {
             /*
@@ -64,7 +72,6 @@ public class Reverses {
                         return classfileBuffer;
                     }
                     className = className.replace("/", ".").replace("\\", ".");
-    
                     // 本项目的印章
                     if (Cache.sealCache == null) {
                         byte[] sealByte = IOUtil.readFileFromWorkbenchRoot(new File(projectPath), Constant.SEAL_FILE);
@@ -77,23 +84,55 @@ public class Reverses {
                         Logger.debug(Reverses.class, "seal of the project is -> " + sealContent);
                         Cache.sealCache = sealContent;
                     }
-                    
+    
+                    /// ============================================ 处理non-class文件
+                    final String inputPwd = javaagentCmdArgs.getPassword();
+                    if (firstExec.compareAndSet(false, true)) {
+                        try {
+                            // 解混淆 除了class文件外的其它文件
+                            Map<String, Pair<byte[], byte[]>> resultMap =
+                                    DecryptExecutor.unMaskNonClassFiles(projectPath, inputPwd == null ? null : inputPwd.toCharArray());
+                            Map<String, byte[]> tmpMap = new HashMap<>(16);
+                            resultMap.forEach((k, v) -> {
+                                tmpMap.put(k, v.getLeft());
+                            });
+                            String finalProjectPath = projectPath;
+                            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+                                try {
+                                    Logger.debug(Reverses.class, "mask non-class files start.");
+                                    JarUtil.rewriteZipEntry(new ZipFile(finalProjectPath), tmpMap);
+                                    Logger.debug(Reverses.class, "mask non-class files end.");
+                                } catch (IOException e) {
+                                    // ignore
+                                }
+                            }));
+                        } catch (Exception e) {
+                            Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
+                            Logger.error(Reverses.class, "Decrypt non-classes fail. ");
+                            System.exit(-1);
+                            // 上一步System.exit(-1)就退出程序了,照理说是不会走到下面这里的(,不过为了以防万一,这里打出提醒, class-winter失效)
+                            for (int i = 0; i < Constant.TEN; i++) {
+                                Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
+                            }
+                            return null;
+                        }
+                    }
+    
+                    /// ============================================ 处理class文件
                     // 判断是否应该解密
                     if (DecryptExecutor.checklistContain(projectPath, className) && DecryptExecutor.verifySeal(classfileBuffer)) {
                         //noinspection DuplicatedCode
                         try {
                             Logger.debug(Reverses.class, "Decrypt class[" + className + "] start.");
                             classfileBuffer = DecryptExecutor.process(projectPath, null, className,
-                                    javaagentCmdArgs.getPassword() == null ? null :
-                                    javaagentCmdArgs.getPassword().toCharArray());
+                                    inputPwd == null ? null :
+                                    inputPwd.toCharArray());
                             Logger.debug(Reverses.class, "Decrypt class[" + className + "] end.");
                             return classfileBuffer;
                         } catch (Exception e) {
-                            if (javaagentCmdArgs.isDebug()) {
-                                Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
-                            }
-                            Logger.error(Reverses.class, "decrypt class[" + className + "] fail. "
-                                    + (StrUtil.isEmpty(javaagentCmdArgs.getPassword()) ? e.getMessage() : "Please ensure your password is correct."));
+                            Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
+                            Logger.error(Reverses.class, "Decrypt class[" + className + "] fail. "
+                                    + (StrUtil.isEmpty(inputPwd) ? e.getMessage() : "Please ensure your password is correct."));
                             System.exit(-1);
                             // 上一步System.exit(-1)就退出程序了,照理说是不会走到下面这里的(,不过为了以防万一,这里打出提醒, class-winter失效)
                             for (int i = 0; i < Constant.TEN; i++) {
@@ -112,11 +151,9 @@ public class Reverses {
                             Logger.debug(Reverses.class, "Decrypt class[" + className + "] end.");
                             return classfileBuffer;
                         } catch (Exception e) {
-                            if (javaagentCmdArgs.isDebug()) {
-                                Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
-                            }
+                            Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
                             String lib = DecryptExecutor.parseLib(classWinterInfoDir);
-                            Logger.error(Reverses.class, "decrypt class[" + className + "] fail. \nPlease check:\n"
+                            Logger.error(Reverses.class, "Decrypt class[" + className + "] fail. \nPlease check:\n"
                                     + "\t1. Ensure 'Your lib " + lib + " need a input password ?'\n"
                                     + "\t2. Ensure 'Your lib "+ lib +"'s password is correct ?'");
                             System.exit(-1);

+ 54 - 4
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/DecryptExecutor.java

@@ -6,10 +6,13 @@ import winter.com.ideaaedi.classwinter.util.Cache;
 import winter.com.ideaaedi.classwinter.util.Constant;
 import winter.com.ideaaedi.classwinter.util.EncryptUtil;
 import winter.com.ideaaedi.classwinter.util.IOUtil;
+import winter.com.ideaaedi.classwinter.util.JarUtil;
 import winter.com.ideaaedi.classwinter.util.Logger;
+import winter.com.ideaaedi.classwinter.util.Pair;
 import winter.com.ideaaedi.classwinter.util.StrUtil;
 
 import java.io.File;
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -17,6 +20,8 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.zip.ZipFile;
 
 /**
  * java class解密
@@ -52,14 +57,15 @@ public class DecryptExecutor {
      * @return  解压后的字节(null-表示classLongName所代表的文件不应该被解密)
      */
     public static byte[] process(String projectPath, String classWinterInfoDir, String classLongName, char[] password) {
+        String baseWinterInfoDir = StrUtil.isBlank(classWinterInfoDir) ? Constant.DEFAULT_ENCRYPTED_BASE_SAVE_DIR : classWinterInfoDir;
         classWinterInfoDir = StrUtil.isBlank(classWinterInfoDir) ? Constant.DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR : classWinterInfoDir;
         // 读取文件
         byte[] bytes = IOUtil.readFileFromWorkbenchRoot(new File(projectPath), classWinterInfoDir + classLongName);
         // 检查密码一致性
-        boolean userIfInputPwdWhileDecrypt = checkPwdConsistency(projectPath, classWinterInfoDir, password);
+        boolean userIfInputPwdWhileDecrypt = checkPwdConsistency(projectPath, baseWinterInfoDir, password);
         // 解密
         return EncryptUtil.decrypt(bytes, userIfInputPwdWhileDecrypt ? password :
-                obtainAutoGeneratedPwdWhileEncrypt(projectPath, classWinterInfoDir));
+                obtainAutoGeneratedPwdWhileEncrypt(projectPath, baseWinterInfoDir));
     }
     
     /**
@@ -115,7 +121,7 @@ public class DecryptExecutor {
                         .forEach(info -> {
                             int idx = info.indexOf("=");
                             if (idx > 0) {
-                                // libDirRelativePath形如: META-INF/winter/abc-1.0.0_jar/
+                                // libDirRelativePath形如: META-INF/winter/classes/abc-1.0.0_jar/
                                 String libDirRelativePath = info.substring(0, idx);
                                 String checklist = info.substring(idx + 1);
                                 if (StrUtil.isBlank(checklist)) {
@@ -236,6 +242,50 @@ public class DecryptExecutor {
         return classWinterInfoDir.replace("_jar/", Constant.JAR_SUFFIX);
     }
     
+    /**
+     * 恢复非class文件的混淆
+     *
+     * @param zipFilePath
+     *            项目jar/war包路径
+     * @param password
+     *            项目加密密码(可能为null)
+     * @return  被替换了的ZipEntry的相对路径及重写前后的内容信息<br/>
+     *          k - ZipEntry的相对路径,如:BOOT-INF/classes/application.yml<br/>
+     *          v - 左:重写前的内容,右:重写后的内容
+     */
+    public static Map<String, Pair<byte[], byte[]>> unMaskNonClassFiles(String zipFilePath, char[] password) throws IOException {
+        final File zipFile = new File(zipFilePath);
+        byte[] nonClassFileChecklistBytes = IOUtil.readFileFromWorkbenchRoot(zipFile, Constant.ALREADY_ENCRYPTED_NON_CLASS_FILE_CHECKLIST_SAVE_FILE);
+        if (nonClassFileChecklistBytes == null) {
+            return new HashMap<>(1);
+        }
+        String nonClassFileChecklist = new String(nonClassFileChecklistBytes, StandardCharsets.UTF_8);
+        Logger.debug(DecryptExecutor.class, "nonClassFileChecklist -> " + nonClassFileChecklist);
+        if (StrUtil.isBlank(nonClassFileChecklist)) {
+            Logger.debug(DecryptExecutor.class, "nonClassFileChecklist is empty.");
+            return new HashMap<>(1);
+        }
+        Set<String> needToDecryptNonClassFileSet = Arrays.stream(nonClassFileChecklist.split(Constant.COMMA)).collect(Collectors.toSet());
+        // 检查密码一致性
+        boolean userIfInputPwdWhileDecrypt = checkPwdConsistency(zipFilePath, Constant.DEFAULT_ENCRYPTED_BASE_SAVE_DIR, password);
+        
+        Map<String, byte[]> replaceMap = new HashMap<>(16);
+        for (String zipEntryName : needToDecryptNonClassFileSet) {
+            // 只处理有印章的
+            byte[] cleanedBytes = IOUtil.readFileFromWorkbenchRoot(zipFile, zipEntryName);
+            if (DecryptExecutor.verifySeal(cleanedBytes)) {
+                byte[] encryptedBytes = IOUtil.readFileFromWorkbenchRoot(zipFile,
+                        Constant.DEFAULT_ENCRYPTED_NON_CLASSES_SAVE_DIR + zipEntryName);
+                byte[] bytes = EncryptUtil.decrypt(encryptedBytes, userIfInputPwdWhileDecrypt ? password :
+                        obtainAutoGeneratedPwdWhileEncrypt(zipFilePath, Constant.DEFAULT_ENCRYPTED_BASE_SAVE_DIR));
+                replaceMap.put(zipEntryName, bytes);
+            }
+        }
+        Logger.debug(DecryptExecutor.class, "un-mask non-classes contains -> " + replaceMap.keySet());
+        return JarUtil.rewriteZipEntry(new ZipFile(zipFilePath), replaceMap);
+    }
+    
+    
     /**
      * 获取class-winter加密时自动生成的密码
      *
@@ -247,7 +297,7 @@ public class DecryptExecutor {
      */
     private static char[] obtainAutoGeneratedPwdWhileEncrypt(String projectPath, String classWinterInfoDir) {
         // 项目本身
-        if (classWinterInfoDir.equals(Constant.DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR)) {
+        if (classWinterInfoDir.equals(Constant.DEFAULT_ENCRYPTED_BASE_SAVE_DIR)) {
             if (Cache.passwordCacheForDecrypt != null) {
                 // 从缓存读取
                 return Cache.passwordCacheForDecrypt;

+ 209 - 20
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/EncryptExecutor.java

@@ -1,12 +1,16 @@
 package winter.com.ideaaedi.classwinter.executor;
 
+import com.sun.org.apache.xerces.internal.impl.Constants;
 import javassist.CannotCompileException;
 import javassist.ClassPool;
 import javassist.NotFoundException;
 import org.dom4j.Document;
+import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.dom4j.io.SAXReader;
 import org.dom4j.io.XMLWriter;
+import org.dom4j.tree.DefaultText;
+import org.xml.sax.SAXException;
 import winter.com.ideaaedi.classwinter.Reverses;
 import winter.com.ideaaedi.classwinter.author.JustryDeng;
 import winter.com.ideaaedi.classwinter.exception.ClassWinterException;
@@ -25,8 +29,12 @@ import winter.com.ideaaedi.classwinter.util.PathUtil;
 import winter.com.ideaaedi.classwinter.util.SimpleFileOrderSupport;
 import winter.com.ideaaedi.classwinter.util.StrUtil;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.lang.reflect.UndeclaredThrowableException;
 import java.nio.charset.StandardCharsets;
 import java.security.SecureRandom;
 import java.util.ArrayList;
@@ -100,6 +108,23 @@ public class EncryptExecutor {
      */
     private final String password;
     
+    
+    /**
+     * 要加密的xml文件的zipEntry名前缀(如:)
+     */
+    private final Set<String> includeXmlPrefixSet;
+    
+    /**
+     * 要排除加密的xml文件的zipEntry名前缀
+     */
+    private final Set<String> excludeXmlPrefixSet;
+    
+    /**
+     * xml中要清除的(根节点的)子节点名称
+     */
+    private final Set<String> toCleanXmlChildElementNameSet;
+
+    
     /**
      * 要加密的类的全类名前缀(也可以精确匹配)
      */
@@ -173,8 +198,10 @@ public class EncryptExecutor {
     private final Map<String, String> libJarAndTmpDirMap = new HashMap<>(8);
     
     private EncryptExecutor(String originJarOrWar, boolean originIsJar, String finalName, String targetRootDir,
-                            String targetLibDir, String targetClassesDir, String password, Set<String> includePrefixSet,
-                            Set<String> excludePrefixSet, Set<String> includeLibSet, Set<Pair<String, String>> alreadyProtectedLibSet, String supportFile) {
+                            String targetLibDir, String targetClassesDir, String password,
+                            Set<String> includeXmlPrefixSet, Set<String> excludeXmlPrefixSet, Set<String> toCleanXmlChildElementNameSet,
+                            Set<String> includePrefixSet, Set<String> excludePrefixSet, Set<String> includeLibSet,
+                            Set<Pair<String, String>> alreadyProtectedLibSet, String supportFile) {
         this.originJarOrWar = originJarOrWar;
         this.originIsJar = originIsJar;
         this.finalName = finalName;
@@ -182,6 +209,9 @@ public class EncryptExecutor {
         this.targetLibDir = targetLibDir;
         this.targetClassesDir = targetClassesDir;
         this.password = password;
+        this.includeXmlPrefixSet = includeXmlPrefixSet;
+        this.excludeXmlPrefixSet = excludeXmlPrefixSet;
+        this.toCleanXmlChildElementNameSet = toCleanXmlChildElementNameSet;
         this.includePrefixSet = includePrefixSet;
         this.excludePrefixSet = excludePrefixSet;
         this.includeLibSet = includeLibSet;
@@ -200,60 +230,126 @@ public class EncryptExecutor {
         
         // step0. 解压jar到jarRootDir下
         List<String> filePathList = JarUtil.unJarWar(originJarOrWar, targetRootDir);
-        showProcess("step00", "step11", "un-jar-war");
-        
+        String lastStep = "step12";
+        showProcess("step00", lastStep, "un-jar-war");
+    
+        File targetRootDirFile = new File(targetRootDir);
+        if (includeXmlPrefixSet != null && includeXmlPrefixSet.size() > 0) {
+            cleanXmlFiles(filePathList, targetRootDirFile);
+        }
+        showProcess("step01", lastStep, "clean-xml-files");
+    
         // step1. jar中的某些lib可能也需要加密,这里先(将那些需要加密的lib)进行解压,(解压到对应的临时目录,以便后面进行加密处理)
         unLibJar();
-        showProcess("step01", "step11", "un-lib-jar");
+        showProcess("step02", lastStep, "un-lib-jar");
         
         // step2. 找到需要加密混淆的class文件
-        File targetRootDirFile = new File(targetRootDir);
         List<File> allFiles = IOUtil.listSubFile(targetRootDirFile, 1);
         List<File> allNeedEncryptedClassFileList = filterClasses(allFiles);
-        showProcess("step02", "step11", "filter-classes");
+        showProcess("step03", lastStep, "filter-classes");
         
         // step3.1. 根据原.class文件,生成加密后的.class文件,并存至Constant.ENCRYPTED_CLASSES_SAVE_DIR
         List<String> allAlreadyEncryptedClassFileList = encryptClasses(allNeedEncryptedClassFileList, new File(targetRootDir,
                 Constant.DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR));
         // step3.2. 生成记录已加密class的全类名的清单文件
         generateChecklistFile(allAlreadyEncryptedClassFileList);
-        showProcess("step03", "step11", "un-jar-war");
+        showProcess("step04", lastStep, "un-jar-war");
         
         // step4. 记录印章(在解密时,可通过 checklist + 印章 判断一个class是否是被class-winter加密过)
         generateSealFile();
-        showProcess("step04", "step11", "encrypt-classes");
+        showProcess("step05", lastStep, "encrypt-classes");
 
         // step5. 混淆原.class文件(即:清空方法体)
         clearClassMethod(allNeedEncryptedClassFileList);
-        showProcess("step05", "step11", "clear-class-method");
+        showProcess("step06", lastStep, "clear-class-method");
     
         // step6. 清除META-INF/maven下pom.xml中关于class-winter-plugin的相关信息
         clearWinterPluginInfo();
-        showProcess("step06", "step11", "clear-winter-plugin-info");
+        showProcess("step07", lastStep, "clear-winter-plugin-info");
         
         // step7. 添加class-winter的用于javaagent解密的代码
         addClassWinterAgent();
-        showProcess("step07", "step11", "add-class-winter-agent");
+        showProcess("step08", lastStep, "add-class-winter-agent");
         
         // step8. 汇总那些在本次加密前本身就已经是被class-winter混淆的lib的相关信息到当前项目中
         collectAlreadyProtectedLibInfo();
-        showProcess("step08", "step11", "collect-already-protected-lib-info");
+        showProcess("step09", lastStep, "collect-already-protected-lib-info");
         
         // step9. 与step1项对应,将临时目录还原为原来的lib(此时得到的lib是加密后的)
         doLibJar();
-        showProcess("step09", "step11", "do-lib-jar");
+        showProcess("step10", lastStep, "do-lib-jar");
         
         // step10. 压缩targetRootDir目录为绝对文件路径名为generatedJarWar的jar(or war)包
         String generatedJarWar = generateJarWarPath(this.originIsJar);
         JarUtil.doJarWar(targetRootDir, generatedJarWar, new SimpleFileOrderSupport(filePathList));
-        showProcess("step10", "step11", "generate-jar-war-path");
+        showProcess("step11", lastStep, "generate-jar-war-path");
         
         // step11. 删除临时目录
         IOUtil.delete(targetRootDirFile);
-        showProcess("step11", "step11", "delete-tmp-dir");
+        showProcess(lastStep, lastStep, "delete-tmp-dir");
         return generatedJarWar;
     }
     
+    /**
+     * 从给定的文件中,找到要混淆的xml文件,并进行混淆
+     *
+     * @param filePathList
+     *            文件路径集合
+     * @param targetRootDirFile
+     *            解压jar/war的根目录
+     */
+    private void cleanXmlFiles(List<String> filePathList, File targetRootDirFile) {
+        Set<String> encryptNonClassFileSet = new HashSet<>();
+        String targetRootBaseDir = targetRootDirFile.getAbsolutePath();
+        // 筛选出需要加密的non-class文件
+        Set<String> allNeedEncryptedNonClassFileSet = filePathList.stream()
+                .filter(x -> x.endsWith(Constant.XML_SUFFIX))
+                .map(filepath -> {
+                    // zipEntryName
+                    String zipEntryName = filepath.replace(targetRootBaseDir, "");
+                    zipEntryName = zipEntryName.replace('\\', '/');
+                    zipEntryName = zipEntryName.startsWith("/") ? zipEntryName.substring(1) : zipEntryName;
+                    return zipEntryName;
+                })
+                .filter(zipEntryName -> {
+                    if (excludeXmlPrefixSet != null) {
+                        boolean shouldExclude = excludeXmlPrefixSet.stream().anyMatch(zipEntryName::startsWith);
+                        if (shouldExclude) {
+                            return false;
+                        }
+                    }
+                    return includeXmlPrefixSet.stream().anyMatch(zipEntryName::startsWith);
+                })
+                .collect(Collectors.toSet());
+        
+        // 加密non-class文件
+        for (String zipEntryName : allNeedEncryptedNonClassFileSet) {
+            // 获取源数据
+            byte[] bytes = IOUtil.readFileFromWorkbenchRoot(targetRootDirFile, zipEntryName);
+            Logger.debug(EncryptExecutor.class, "Encrypt non-class[" + zipEntryName + "] start.");
+            // 加密
+            final byte[] encryptedBytes = EncryptUtil.encrypt(bytes, obtainPassword());
+            // 将加密后的数据文件存储到指定位置
+            IOUtil.toFile(encryptedBytes,
+                    new File(this.targetRootDir + File.separator + Constant.DEFAULT_ENCRYPTED_NON_CLASSES_SAVE_DIR, zipEntryName),
+                    true);
+            Logger.debug(EncryptExecutor.class, "Encrypt non-class[" + zipEntryName + "] end.");
+            encryptNonClassFileSet.add(zipEntryName);
+        }
+        
+        // 生成清单文件
+        String nonClassChecklist = String.join(Constant.COMMA, encryptNonClassFileSet);
+        IOUtil.writeContentToFile(nonClassChecklist,
+                new File(this.targetRootDir, Constant.ALREADY_ENCRYPTED_NON_CLASS_FILE_CHECKLIST_SAVE_FILE));
+        
+        // 清空原xml文件的关键节点
+        for (String zipEntryName : encryptNonClassFileSet) {
+            byte[] bytes = IOUtil.readFileFromWorkbenchRoot(targetRootDirFile, zipEntryName);
+            String cleanedXml = clearXml(new String(bytes, StandardCharsets.UTF_8), "\n\t\t\t" + Constant.TIPS + "\n\t\t\t" + Constant.SEAL, toCleanXmlChildElementNameSet);
+            IOUtil.writeContentToFile(cleanedXml, new File(targetRootDir, zipEntryName));
+        }
+    }
+    
     /**
      * showProcess
      *
@@ -397,7 +493,7 @@ public class EncryptExecutor {
                 return;
             } else {
                 String libChecklist = new String(checklistByte, StandardCharsets.UTF_8);
-                IOUtil.writeContentToFile(libChecklist, new File(dirAbsolutePathForLib, Constant.CHECKLIST_FILE_SIMPLE_NAME));
+                IOUtil.writeContentToFile(libChecklist, new File(dirAbsolutePathForLib, Constant.CHECKLIST_CLASS_FILE_SIMPLE_NAME));
     
                 // 所有lib的checklist汇总至checklistOfAllLibs
                 allChecklistInfoMap.put(dirRelativePathForLib, libChecklist);
@@ -733,6 +829,68 @@ public class EncryptExecutor {
                 }).collect(Collectors.toList());
     }
     
+    /**
+     * 清空xml中指定的一级子节点内容,并以指定的内容进行注释填充
+     *
+     * @param xmlContent
+     *            原xml内容
+     * @param paddingComment
+     *            填充注释内容
+     * @param toClearFirstNodeNameSet
+     *            一级子节点名称
+     * @return  清空后的xml内容
+     */
+    private String clearXml(String xmlContent, String paddingComment, Set<String> toClearFirstNodeNameSet) {
+        if (toClearFirstNodeNameSet == null || toClearFirstNodeNameSet.size() == 0) {
+            return xmlContent;
+        }
+        if (xmlContent == null || xmlContent.trim().length() == 0) {
+            return xmlContent;
+        }
+        ByteArrayOutputStream os = null;
+        try {
+            SAXReader reader = new SAXReader();
+            // 不校验xml头部
+            reader.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE, false);
+            Document document = reader.read(new StringReader(xmlContent));
+            Element rootElement = document.getRootElement();
+            if (rootElement == null) {
+                return xmlContent;
+            }
+            //noinspection rawtypes
+            Iterator iterator = rootElement.elementIterator();
+            if (iterator == null) {
+                return null;
+            }
+            while (iterator.hasNext()) {
+                Element element = (Element) iterator.next();
+                if (element == null) {
+                    continue;
+                }
+                if (!toClearFirstNodeNameSet.contains(element.getName())) {
+                    continue;
+                }
+                element.clearContent();
+                element.add(new DefaultText("\n\t\t"));
+                element.addComment(paddingComment + "\n\t\t");
+                element.add(new DefaultText("\n\t"));
+            }
+            os = new ByteArrayOutputStream();
+            XMLWriter xmlWriter = new XMLWriter(os);
+            xmlWriter.write(document);
+            xmlWriter.flush();
+            xmlWriter.close();
+            return new String(os.toByteArray(), StandardCharsets.UTF_8);
+        } catch (SAXException | DocumentException |IOException e) {
+            throw new UndeclaredThrowableException(e,
+                    String.format("clear xmlContent exception. \nxmlContent -> %s, \npaddingComment -> %s, \ntoClearFirstNodeNameSet -> %s",
+                            xmlContent, paddingComment, toClearFirstNodeNameSet));
+        } finally {
+            IOUtil.close(os);
+        }
+    }
+    
+    
     
     @Override
     public String toString() {
@@ -747,8 +905,11 @@ public class EncryptExecutor {
                 ", password='" + (StrUtil.isBlank(password) ? null : "******") + '\'' +
                 ", supportFile='" + supportFile + '\'' +
                 ", includePrefixSet=" + includePrefixSet +
-                ", includeLibSet=" + includeLibSet +
                 ", excludePrefixSet=" + excludePrefixSet +
+                ", includeXmlPrefixSet=" + includeXmlPrefixSet +
+                ", excludeXmlPrefixSet=" + excludeXmlPrefixSet +
+                ", toCleanChildElementNameSet=" + toCleanXmlChildElementNameSet +
+                ", includeLibSet=" + includeLibSet +
                 ", alreadyProtectedLibSet=" + alreadyProtectedLibSet +
                 '}';
     }
@@ -772,6 +933,12 @@ public class EncryptExecutor {
         
         private String excludePrefix;
         
+        private String includeXmlPrefix;
+        
+        private String excludeXmlPrefix;
+        
+        private String toCleanXmlChildElementName;
+        
         private String includeLibs;
         
         private String alreadyProtectedLibs;
@@ -806,6 +973,21 @@ public class EncryptExecutor {
             this.excludePrefix = excludePrefix;
             return this;
         }
+    
+        public Builder includeXmlPrefix(String includeXmlPrefix) {
+            this.includeXmlPrefix = includeXmlPrefix;
+            return this;
+        }
+        
+        public Builder excludeXmlPrefix(String excludeXmlPrefix) {
+            this.excludeXmlPrefix = excludeXmlPrefix;
+            return this;
+        }
+        
+        public Builder toCleanXmlChildElementName(String toCleanXmlChildElementName) {
+            this.toCleanXmlChildElementName = toCleanXmlChildElementName;
+            return this;
+        }
         
         public Builder includeLibs(String includeLibs) {
             this.includeLibs = includeLibs;
@@ -838,7 +1020,8 @@ public class EncryptExecutor {
             Logger.ENABLE_DEBUG.set(this.debug != null && this.debug);
             // 2. 错误启动jar时,System.out输出的提示信息
             if (!StrUtil.isEmpty(tips)) {
-                JavassistUtil.TIPS = tips;
+                Constant.TIPS.setLength(0);
+                Constant.TIPS.append(tips);
             }
             
             // ---- 参数校验
@@ -917,8 +1100,14 @@ public class EncryptExecutor {
                 }
                 throw new ClassWinterException("includeLibs format must be shaped like xxx1.jar[,xxx2.jar,xxx3.jar]");
             }
+            
+            // xml相关参数解析
+            Set<String> includeXmlPrefixSet = StrUtil.strToSet(includeXmlPrefix);
+            Set<String> excludeXmlPrefixSet = StrUtil.strToSet(excludeXmlPrefix);
+            toCleanXmlChildElementName = StrUtil.isBlank(toCleanXmlChildElementName) ? Constant.DEFAULT_XML_NODE_NAMES : toCleanXmlChildElementName;
+            Set<String> toCleanXmlChildElementNameSet = StrUtil.strToSet(toCleanXmlChildElementName);
             return new EncryptExecutor(this.originJarOrWar, originIsJar, this.finalName, targetRootDir, targetLibDir, targetClassesDir,
-                    password, includePrefixSet, excludePrefixSet, includeLibSet, alreadyProtectedLibSet, this.supportFile);
+                    password, includeXmlPrefixSet, excludeXmlPrefixSet, toCleanXmlChildElementNameSet, includePrefixSet, excludePrefixSet, includeLibSet, alreadyProtectedLibSet, this.supportFile);
         }
     
         /**

+ 34 - 8
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/Constant.java

@@ -13,10 +13,21 @@ import java.time.format.DateTimeFormatter;
  */
 public interface Constant {
     
+    /**
+     * 被混淆了的内容的提示信息
+     */
+    StringBuffer TIPS = new StringBuffer("ERROR !!!!!!!!!!! Jar(or War) has been protected by class-winter. Please use javaagent re-start project. !!!!!!!!!!!");
+    
+    
     /**
      * 印章, 若class中存在此印章内容,也能说明该class被class-winter加密过
      */
-    String SEAL = "At " + LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME) + "\t" + "Random-Character " + new String(EncryptUtil.generateCharArr(32));
+    String SEAL = "At " + LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME).replace("T"," ") + "\t" + "Random-Character " + new String(EncryptUtil.generateCharArr(32));
+    
+    /**
+     * 默认需要加密的xml一级节点名称
+     */
+    String DEFAULT_XML_NODE_NAMES = "resultMap,sql,insert,update,delete,select";
     
     /**
      * 解压jar/war包时,临时目录后缀名
@@ -34,24 +45,34 @@ public interface Constant {
     String CLASSES = "classes";
     
     /**
-     * 默认的class-winter相关信息的存储目录
+     * 默认的class-winter加密存储目录base
+     */
+    String DEFAULT_ENCRYPTED_BASE_SAVE_DIR = "META-INF/winter/";
+    
+    /**
+     * 默认的class-winter加密classes相关信息的存储目录
      */
-    String DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR = "META-INF/winter/";
+    String DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR = "META-INF/winter/classes/";
     
     /**
-     * MANIFEST.MF文件的位置
+     * 默认的class-winter加密其它文件(如.yml .yaml .xml .properties)相关信息的存储目录
      */
-    String MANIFEST_FILE = "META-INF/MANIFEST.MF";
+    String DEFAULT_ENCRYPTED_NON_CLASSES_SAVE_DIR = "META-INF/winter/non-classes/";
     
     /**
      * 记录已加密.class的全类名的清单文件
      */
-    String ALREADY_ENCRYPTED_CLASS_CHECKLIST_CLASSES_SAVE_FILE = "META-INF/winter/checklist.winter";
+    String ALREADY_ENCRYPTED_CLASS_CHECKLIST_CLASSES_SAVE_FILE = "META-INF/winter/checklist.classes.winter";
+    
+    /**
+     * 记录已加密的非class文件的条目名清单文件(内容形如: BOOT-INF/classes/application.properties,BOOT-INF/classes/application-dev.properties)
+     */
+    String ALREADY_ENCRYPTED_NON_CLASS_FILE_CHECKLIST_SAVE_FILE = "META-INF/winter/checklist.non-classes.winter";
     
     /**
      * 记录已加密.class的全类名的清单文件
      */
-    String CHECKLIST_FILE_SIMPLE_NAME = "checklist.winter";
+    String CHECKLIST_CLASS_FILE_SIMPLE_NAME = "checklist.classes.winter";
     
     /**
      * 记录本次加密印章的文件
@@ -71,7 +92,7 @@ public interface Constant {
     /**
      * 汇总记录项目中那些本身就已经被class-winter混淆了的lib的checklist
      */
-    String CHECKLIST_OF_ALL_LIBS = "META-INF/winter/checklist-of-all-libs.winter";
+    String CHECKLIST_OF_ALL_LIBS = DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR + "checklist-of-all-libs.winter";
     
     /**
      * 当用户不主动指定密码时,class_winter会自动生成加密密码,并存至此处
@@ -113,6 +134,11 @@ public interface Constant {
      */
     String JAR_SUFFIX = ".jar";
     
+    /**
+     * xml文件后缀
+     */
+    String XML_SUFFIX = ".xml";
+    
     /**
      * war包后缀
      */

+ 61 - 3
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/JarUtil.java

@@ -17,6 +17,7 @@ import java.util.Collection;
 import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -87,7 +88,6 @@ public final class JarUtil {
                     zipEntry.setTime(file.lastModified());
                     zipEntry.setLastModifiedTime(FileTime.fromMillis(file.lastModified()));
                     zos.putNextEntry(zipEntry);
-                    zos.closeEntry();
                 }
                 // jar文件, 需要写CRC32信息
                 else if (fileName.endsWith(Constant.JAR_SUFFIX)) {
@@ -100,7 +100,6 @@ public final class JarUtil {
                     ze.setLastModifiedTime(FileTime.fromMillis(file.lastModified()));
                     zos.putNextEntry(ze);
                     zos.write(bytes);
-                    zos.closeEntry();
                 }
                 // 其它文件直接写入
                 else {
@@ -110,8 +109,8 @@ public final class JarUtil {
                     zos.putNextEntry(zipEntry);
                     byte[] bytes = IOUtil.toBytes(file);
                     zos.write(bytes);
-                    zos.closeEntry();
                 }
+                zos.closeEntry();
             }
         } catch (IOException e) {
             throw new ClassWinterException(e);
@@ -248,6 +247,65 @@ public final class JarUtil {
         return targetFile.getAbsolutePath();
     }
     
+    /**
+     * 修改zip文件(.java、.war文件)中的条目
+     *
+     * @param zipFile
+     *            要修改的zip文件(.java、.war文件)
+     * @param replacerMap
+     *            替换器(k-ZipFile中,要被替换的ZipEntry的相对路径,如:BOOT-INF/classes/application.yml; V-要替换成的内容)
+     * @return  被替换了的ZipEntry的相对路径及重写前后的内容信息<br/>
+     *          k - ZipEntry的相对路径,如:BOOT-INF/classes/application.yml<br/>
+     *          v - 左:重写前的内容,右:重写后的内容
+     */
+    public static Map<String, Pair<byte[], byte[]>> rewriteZipEntry(ZipFile zipFile, Map<String, byte[]> replacerMap) throws IOException {
+        Map<String, Pair<byte[], byte[]>> map = new HashMap<>(8);
+        if (replacerMap == null || replacerMap.size() == 0) {
+            return map;
+        }
+        if (zipFile == null) {
+            Logger.warn("zipFile is null.");
+            return map;
+        }
+        String zipFilePath = zipFile.getName();
+        if (!new File(zipFilePath).exists()) {
+            Logger.warn("zipFile [" + zipFilePath + "] non-exist.");
+            return map;
+        }
+        List<ZipEntry> zipEntryList = new LinkedList<>();
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry zipEntry = entries.nextElement();
+            zipEntryList.add(zipEntry);
+        }
+        // zipFile.getName()形如:   /abc/my-project.jar   /abc/my-project.war  /abc/my-projectzip
+        try (FileOutputStream fos = new FileOutputStream(zipFilePath, true);
+             ZipOutputStream zos = new ZipOutputStream(fos)) {
+            for (ZipEntry zipEntry : zipEntryList) {
+                final String zipEntryName = zipEntry.getName();
+                final byte[] originBytes = IOUtil.toBytes(zipFile.getInputStream(zipEntry));
+                final byte[] replaceBytes = replacerMap.get(zipEntryName);
+                if (replacerMap.containsKey(zipEntryName) && replaceBytes != null) {
+                    // 覆盖
+                    final ZipEntry ze = new ZipEntry(zipEntryName);
+                    ze.setMethod(ZipEntry.STORED);
+                    ze.setSize(replaceBytes.length);
+                    ze.setCrc(IOUtil.computeCrc32(replaceBytes));
+                    ze.setTime(zipEntry.getTime());
+                    ze.setLastModifiedTime(zipEntry.getLastModifiedTime());
+                    zos.putNextEntry(ze);
+                    zos.write(replaceBytes);
+                    map.put(zipEntryName, Pair.of(originBytes, replaceBytes));
+                } else {
+                    // 其它的不动
+                    zos.putNextEntry(new ZipEntry(zipEntry));
+                    zos.write(originBytes);
+                }
+            }
+        }
+        return map;
+    }
+    
     /**
      * 从(zip/jar/war)压缩文件中获取一个文件的字节
      * <p>

+ 3 - 8
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/JavassistUtil.java

@@ -30,12 +30,7 @@ import java.util.stream.Collectors;
  * @since 2021/4/25 20:54:30
  */
 public final class JavassistUtil {
-    
-    /**
-     * 被清空了方法体的方法内部的提示信息
-     */
-    public static String TIPS = "ERROR !!!!!!!!!!! Jar(or War) has been protected by class-winter. Please use javaagent re-start project. !!!!!!!!!!!";
-    
+
     /**
      * 清空类中的方法体,并简单处理一下main,使提示密码无效
      *
@@ -83,9 +78,9 @@ public final class JavassistUtil {
                     /// }
                     try {
                         ctMethod.insertBefore(
-                                "System.out.println(\"\\n " + TIPS +" \\n\");"
+                                "System.out.println(\"\\n " + Constant.TIPS +" \\n\");"
                                 // 也输出印章信息吧
-                                + "\nSystem.out.println(\"" + Constant.SEAL +"\");"
+                                + "\nSystem.out.println(\" " + Constant.SEAL +" \");"
                                 + "\nSystem.exit(-1);"
                         );
                     } catch (CannotCompileException e) {

+ 44 - 3
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/AllTest.java

@@ -18,9 +18,50 @@ import winter.com.ideaaedi.classwinter.util.PathUtil;
 public class AllTest {
     
     public static void main(String[] args) {
+//        xmlTest();
+        otherTest();
+    }
+    
+    /**
+     * 直接加密测试
+     */
+    private static void encryptDirectly() {
+        String projectRootDir = PathUtil.getProjectRootDir(AllTest.class);
+        String originJarOrWar = projectRootDir + "my-project-with-encrypted-lib-have-pwd.jar";
+        String includePrefix = "com.szlaozicl,com.aspire.ssm,org.springframework.aop.target.dynamic,com.fasterxml.jackson";
+        String excludePrefix = "com.aspire.ssm.config,com.aspire.ssm.util.Person";
+        String finalName = "jd";
+        String includeLibs = "spring-aop-5.2.7.RELEASE.jar,jackson-core-2.10.4.jar";
+        String tips = "方法已经被class-winter加密保护了,请不要直接使用java -jar xxx.jar启动项目,请使用java -javaagent:xxx.jar -jar xxx.jar启动项目.";
+        String alreadyProtectedLibs = "encrypted-lib-have-pwd-1.0.0.jar:qwer123~";
+        String debug = "true";
+        
+        Forward.main(new String[]{
+                "originJarOrWar=" + originJarOrWar,
+                "includePrefix=" + includePrefix,
+                "excludePrefix=" + excludePrefix,
+                "finalName=" + finalName,
+                "includeLibs=" + includeLibs,
+                "tips=" + tips,
+                "alreadyProtectedLibs=" + alreadyProtectedLibs,
+                "debug=" + debug
+        });
+    }
+    
+    /**
+     * xml测试
+     */
+    private static void xmlTest() {
+        Xml_Test.main(null);
+    }
+    
+    /**
+     * 其它测试
+     */
+    private static void otherTest() {
         // 杀下进程(以保证端口没有被占用)
         BashUtil.killProcessByPorts("8080");
-
+        
         // 加密
         String projectRootDir = PathUtil.getProjectRootDir(AllTest.class);
         String originJarOrWar = projectRootDir + "my-project-with-encrypted-lib-have-pwd.jar";
@@ -32,7 +73,7 @@ public class AllTest {
         String tips = "方法已经被class-winter加密保护了,请不要直接使用java -jar xxx.jar启动项目,请使用java -javaagent:xxx.jar -jar xxx.jar启动项目.";
         String alreadyProtectedLibs = "encrypted-lib-have-pwd-1.0.0.jar:qwer123~";
         String debug = "true";
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " excludePrefix=" + excludePrefix
@@ -45,7 +86,7 @@ public class AllTest {
                 + " debug=" + debug
                 ;
         BashUtil.runBashAndPrint(startBat);
-
+        
         String encryptedJar = projectRootDir + finalName + ".jar";
         // 解密(模拟启动加密后的jar包)
         // String javaagentArgs = "";

+ 2 - 2
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/AlreadyProtectedLibs_Test.java

@@ -67,7 +67,7 @@ public class AlreadyProtectedLibs_Test {
         String originJarOrWar = projectRootDir + Constant.LINUX_FILE_SEPARATOR + "my-project-with-encrypted-lib-no-pwd.jar";
         String includePrefix = "non-exist";
         String alreadyProtectedLibs = "encrypted-lib-no-pwd-1.0.0.jar";
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " alreadyProtectedLibs=" + alreadyProtectedLibs
@@ -110,7 +110,7 @@ public class AlreadyProtectedLibs_Test {
         String includePrefix = "non-exist";
         String alreadyProtectedLibs = "encrypted-lib-have-pwd-1.0.0.jar:qwer123~";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " alreadyProtectedLibs=" + alreadyProtectedLibs

+ 2 - 2
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Debug_Test.java

@@ -36,7 +36,7 @@ public class Debug_Test {
         String originJarOrWar = projectRootDir + "boot-jar.jar";
         String includePrefix = "com";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 ;
@@ -64,7 +64,7 @@ public class Debug_Test {
         String originJarOrWar = projectRootDir + "boot-jar.jar";
         String includePrefix = "com";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true

+ 2 - 2
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/ExcludePrefix_Test.java

@@ -38,7 +38,7 @@ public class ExcludePrefix_Test {
         String originJarOrWar = projectRootDir + "boot-jar.jar";
         String includePrefix = "com.aspire.ssm.util.AesEncryptDecryptUtil,com.aspire.ssm.util.DbSecurityBeanUtil";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true
@@ -72,7 +72,7 @@ public class ExcludePrefix_Test {
         String includePrefix = "com.aspire.ssm.util.AesEncryptDecryptUtil,com.aspire.ssm.util.DbSecurityBeanUtil";
         String excludePrefix = "com.aspire.ssm.util.DbSecurityBeanUtil";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " excludePrefix=" + excludePrefix

+ 1 - 1
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/FinalName_Test.java

@@ -23,7 +23,7 @@ public class FinalName_Test {
         String includePrefix = "com";
         String finalName = "YiYaKaKa";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " finalName=" + finalName

+ 1 - 1
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/IncludeLibs_Test.java

@@ -42,7 +42,7 @@ public class IncludeLibs_Test {
         String includePrefix = "org.springframework.aop.target.dynamic,com.google.common.xml";
         String includeLibs = "spring-aop-5.1.8.RELEASE.jar,guava-28.2-jre.jar";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " includeLibs=" + includeLibs

+ 1 - 1
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/IncludePrefix_Test.java

@@ -39,7 +39,7 @@ public class IncludePrefix_Test {
         String originJarOrWar = projectRootDir + "boot-jar.jar";
         String includePrefix = "com.aspire.ssm.util";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true

+ 2 - 2
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/OriginJarOrWar_Test.java

@@ -32,7 +32,7 @@ public class OriginJarOrWar_Test {
         String originJarOrWar = projectRootDir + "non-exist.jar";
         String includePrefix = "com.aspire.ssm.util";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true
@@ -58,7 +58,7 @@ public class OriginJarOrWar_Test {
         String originJarOrWar = projectRootDir + "boot-jar.jar";
         String includePrefix = "com.aspire.ssm.util";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true

+ 1 - 1
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Password_Test.java

@@ -23,7 +23,7 @@ public class Password_Test {
         String includePrefix = "com";
         String password = "pwd123";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " password=" + password

+ 2 - 2
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/SupportFile_Test.java

@@ -42,7 +42,7 @@ public class SupportFile_Test {
         String originJarOrWar = projectRootDir + "normal-jar.jar";
         String includePrefix = "com";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 ;
@@ -62,7 +62,7 @@ public class SupportFile_Test {
         String includePrefix = "com";
         String supportFile = projectRootDir + "help";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " supportFile=" + supportFile

+ 3 - 3
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Tips_Test.java

@@ -32,7 +32,7 @@ public class Tips_Test {
         String originJarOrWar = projectRootDir + "boot-jar.jar";
         String includePrefix = "com";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 ;
@@ -55,8 +55,8 @@ public class Tips_Test {
         String tips = "[潘嘎之交] 潘: 你别怪潘叔说话比较直白, 网络上的东西都是虚拟的, 你把握不住, 孩子! 因为这里的水很深...";
     
         // 如果tips值由一些特殊字符(如空格等)的话,可以用windows双引号(linux单引号)引起来。
-        // java -jar class-winter-core-2.0.0.jar originJarOrWar=/boot-jar.jar includePrefix=com tips="[潘嘎之交] 潘: 你别怪潘叔说话比较直白, 网络上的东西都是虚拟的, 你把握不住, 孩子! 因为这里的水很深..."
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        // java -jar class-winter-core-2.1.0.jar originJarOrWar=/boot-jar.jar includePrefix=com tips="[潘嘎之交] 潘: 你别怪潘叔说话比较直白, 网络上的东西都是虚拟的, 你把握不住, 孩子! 因为这里的水很深..."
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " tips=" + (BashUtil.IS_WINDOWS ? "\"".concat(tips).concat("\"") : "'".concat(tips).concat("'"));

+ 10 - 10
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/War_Test.java

@@ -34,7 +34,7 @@ public class War_Test {
         String originJarOrWar = projectRootDir + "war-project.war";
         String includePrefix = "com";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 ;
@@ -43,7 +43,7 @@ public class War_Test {
         
         String encryptedWar = originJarOrWar.replace(".war", "-encrypted.war");
         System.out.println();
-        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
+        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
         System.out.println("其中参数附加 -javaagent:class-winter-core.jar");
         // System.out.println("其中参数附加 -javaagent:class-winter-core.jar=debug=true,password=pwd123");
         System.out.println();
@@ -62,7 +62,7 @@ public class War_Test {
         String includePrefix = "com";
         String password = "123456";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " password=" + password
@@ -73,7 +73,7 @@ public class War_Test {
     
         String encryptedWar = originJarOrWar.replace(".war", "-encrypted.war");
         System.out.println();
-        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
+        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
         System.out.println("其中参数附加 -javaagent:class-winter-core.jar=password=" + password);
         // System.out.println("其中参数附加 -javaagent:class-winter-core.jar=debug=true,password=pwd123");
         System.out.println();
@@ -93,7 +93,7 @@ public class War_Test {
         String alreadyProtectedLibs = "encrypted-lib-no-pwd.jar";
         String password = "oop857857";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " alreadyProtectedLibs=" + alreadyProtectedLibs
@@ -104,7 +104,7 @@ public class War_Test {
     
         String encryptedWar = originJarOrWar.replace(".war", "-encrypted.war");
         System.out.println();
-        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
+        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
         System.out.println("其中参数附加 -javaagent:class-winter-core.jar=password=" + password);
         // System.out.println("其中参数附加 -javaagent:class-winter-core.jar=debug=true,password=pwd123");
         System.out.println();
@@ -124,7 +124,7 @@ public class War_Test {
         String alreadyProtectedLibs = "encrypted-lib-have-pwd.jar:qwer123~";
         String password = "yiyakaka";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " alreadyProtectedLibs=" + alreadyProtectedLibs
@@ -135,7 +135,7 @@ public class War_Test {
     
         String encryptedWar = originJarOrWar.replace(".war", "-encrypted.war");
         System.out.println();
-        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
+        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedWar);
         System.out.println("其中参数附加 -javaagent:class-winter-core.jar=password=" + password);
         // System.out.println("其中参数附加 -javaagent:class-winter-core.jar=debug=true,password=pwd123");
         System.out.println();
@@ -160,7 +160,7 @@ public class War_Test {
         String alreadyProtectedLibs = "encrypted-lib-have-pwd.jar:qwer123~";
         String debug = "true";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.0.0.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " excludePrefix=" + excludePrefix
@@ -177,7 +177,7 @@ public class War_Test {
     
         String encryptedJar = projectRootDir + finalName + ".war";
         System.out.println();
-        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedJar);
+        System.out.println("[加密完成] 请使用web容器(如tomcat)启动war包:" + encryptedJar);
         System.out.println("其中参数附加 -javaagent:class-winter-core.jar=password=pwd12345");
         // System.out.println("其中参数附加 -javaagent:class-winter-core.jar=debug=true,password=pwd123");
         System.out.println();

+ 51 - 0
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Xml_Test.java

@@ -0,0 +1,51 @@
+package winter.com.ideaaedi.classwinter;
+
+import winter.com.ideaaedi.classwinter.author.JustryDeng;
+import winter.com.ideaaedi.classwinter.util.BashUtil;
+import winter.com.ideaaedi.classwinter.util.PathUtil;
+
+/**
+ * 这里只讲war包进行混淆, 需要执行使用web容器(如tomcat)启动war包
+ *
+ * @author {@link JustryDeng}
+ * @since 2021/6/12 14:34:45
+ */
+public class Xml_Test {
+    
+    public static void main(String[] args) {
+        // 杀下进程(以保证端口没有被占用)
+        BashUtil.killProcessByPorts("8080");
+        test();
+    }
+    
+    /**
+     * 期望结果:
+     *    项目正常启动,启动成功后(通过走xml的查询语句)能查出数据
+     */
+    public static void test() {
+        String projectRootDir = PathUtil.getProjectRootDir(AlreadyProtectedLibs_Test.class);
+        String originJarOrWar = projectRootDir + "xml-test-project.jar";
+        String includePrefix = "com";
+        String includeXmlPrefix = "BOOT-INF/classes/";
+        String excludeXmlPrefix = "BOOT-INF/classes/resources/";
+        String toCleanXmlChildElementName = "select";
+        
+        
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.1.0.jar"
+                + " originJarOrWar=" + originJarOrWar
+                + " includePrefix=" + includePrefix
+                + " includeXmlPrefix=" + includeXmlPrefix
+                + " excludeXmlPrefix=" + excludeXmlPrefix
+                + " toCleanXmlChildElementName=" + toCleanXmlChildElementName
+                ;
+        // 加密
+        BashUtil.runBashAndPrint(startBat);
+        
+        System.out.println();
+        // 解密启动
+        String encryptedJar = originJarOrWar.replace(".jar", "-encrypted.jar");
+        String javaagentArgs = "=debug=true";
+        // String javaagentArgs = "=debug=true,password=xxx";
+        BashUtil.runBashAndPrint(String.format("java -javaagent:%s%s -jar %s", encryptedJar, javaagentArgs, encryptedJar));
+    }
+}

BIN
class-winter-core/src/test/resources/class-winter-core-1.1.0.jar


BIN
class-winter-core/src/test/resources/class-winter-core-1.2.0.jar


BIN
class-winter-core/src/test/resources/class-winter-core-1.2.1.jar


BIN
class-winter-core/src/test/resources/class-winter-core-1.2.2.jar


BIN
class-winter-core/src/test/resources/class-winter-core-1.2.3.jar → class-winter-core/src/test/resources/class-winter-core-2.1.0.jar


BIN
class-winter-core/src/test/resources/my-project-with-encrypted-lib-have-pwd.jar


BIN
class-winter-core/src/test/resources/my-project-with-encrypted-lib-have-pwd.war


BIN
class-winter-core/src/test/resources/my-project-with-encrypted-lib-no-pwd.jar


BIN
class-winter-core/src/test/resources/my-project-with-encrypted-lib-no-pwd.zip


BIN
class-winter-core/src/test/resources/xml-test-project.jar


+ 1 - 1
class-winter-maven-plugin/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.idea-aedi</groupId>
         <artifactId>class-winter</artifactId>
-        <version>2.0.0</version>
+        <version>2.1.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 15 - 0
class-winter-maven-plugin/src/main/java/winter/com/ideaaedi/classwinter/plugin/ClassWinterPlugin.java

@@ -41,6 +41,15 @@ public class ClassWinterPlugin extends AbstractMojo {
     @Parameter
     private String excludePrefix;
     
+    @Parameter
+    private String includeXmlPrefix;
+    
+    @Parameter
+    private String excludeXmlPrefix;
+    
+    @Parameter
+    private String toCleanXmlChildElementName;
+    
     @Parameter
     private String includeLibs;
     
@@ -65,6 +74,9 @@ public class ClassWinterPlugin extends AbstractMojo {
         Logger.debug("You config arg originJarOrWar -> " + originJarOrWar);
         Logger.debug("You config arg includePrefix -> " + includePrefix);
         Logger.debug("You config arg excludePrefix -> " + excludePrefix);
+        Logger.debug("You config arg includeXmlPrefix -> " + includeXmlPrefix);
+        Logger.debug("You config arg excludeXmlPrefix -> " + excludeXmlPrefix);
+        Logger.debug("You config arg toCleanXmlChildElementName -> " + toCleanXmlChildElementName);
         Logger.debug("You config arg finalName -> " + finalName);
         Logger.debug("You config arg password -> " + password);
         Logger.debug("You config arg includeLibs -> " + includeLibs);
@@ -82,6 +94,9 @@ public class ClassWinterPlugin extends AbstractMojo {
                 .password(password)
                 .includePrefix(includePrefix)
                 .excludePrefix(excludePrefix)
+                .includeXmlPrefix(includeXmlPrefix)
+                .excludeXmlPrefix(excludeXmlPrefix)
+                .toCleanXmlChildElementName(toCleanXmlChildElementName)
                 .includeLibs(includeLibs)
                 .alreadyProtectedLibs(alreadyProtectedLibs)
                 .supportFile(supportFile)

+ 1 - 1
pom.xml

@@ -9,7 +9,7 @@
     </modules>
     <groupId>com.idea-aedi</groupId>
     <artifactId>class-winter</artifactId>
-    <version>2.0.0</version>
+    <version>2.1.0</version>
 
     <!-- 配置项目的基础信息 -->
     <name>class-winter</name>