Ver Fonte

Merge branch 'master' into release-2.8.0

JustryDeng há 3 anos atrás
pai
commit
9dc1a748f0
30 ficheiros alterados com 592 adições e 166 exclusões
  1. 58 26
      README.md
  2. 1 1
      class-winter-core/pom.xml
  3. 2 2
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/Forward.java
  4. 59 36
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/Reverses.java
  5. 24 19
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/DecryptExecutor.java
  6. 106 30
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/EncryptExecutor.java
  7. 10 4
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/Cache.java
  8. 21 0
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/Constant.java
  9. 104 0
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/EncryptClassArgs.java
  10. 73 16
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/JavassistUtil.java
  11. 14 0
      class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/StrUtil.java
  12. 2 2
      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. 88 0
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/EncryptArgParam_Test.java
  16. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/ExcludePrefix_Test.java
  17. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/FinalName_Test.java
  18. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/IncludeLibs_Test.java
  19. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/IncludePrefix_Test.java
  20. 3 3
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/JvmArgCheck_Test.java
  21. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/OriginJarOrWar_Test.java
  22. 3 3
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Password_Test.java
  23. 2 2
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/SupportFile_Test.java
  24. 3 3
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Tips_Test.java
  25. 5 5
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/War_Test.java
  26. 1 1
      class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/Xml_Test.java
  27. BIN
      class-winter-core/src/test/resources/class-winter-core-2.8.0.jar
  28. BIN
      class-winter-core/src/test/resources/class-winter-core-enhance-2.8.0.jar
  29. 1 1
      class-winter-maven-plugin/pom.xml
  30. 1 1
      pom.xml

+ 58 - 26
README.md

@@ -52,7 +52,7 @@
   <plugin>
       <groupId>com.idea-aedi</groupId>
       <artifactId>class-winter-maven-plugin</artifactId>
-      <version>2.7.9</version>
+      <version>2.8.0</version>
       <!-- 相关配置 -->
       <configuration>
           <!-- <finalName></finalName>-->
@@ -83,15 +83,15 @@
   ```
   注:不必担心信息泄漏问题,使用此方式生成混淆的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.7.9.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.8.0.jar) 主动加密</font>
 
   ```bash
-  java -jar class-winter-core-2.7.9.jar originJarOrWar=${要加密的项目.jar或.war包} includePrefix=${加密范围} [k3=v3 k4=v4 ...]
+  java -jar class-winter-core-2.8.0.jar originJarOrWar=${要加密的项目.jar或.war包} includePrefix=${加密范围} [k3=v3 k4=v4 ...]
   # 对于复杂的参数值,可以使用引号引起来
   # linux
-  java -jar class-winter-core-2.7.9.jar k1='v1' k2='v2'
+  java -jar class-winter-core-2.8.0.jar k1='v1' k2='v2'
   # windows
-  java -jar class-winter-core-2.7.9.jar k1="v1" k2="v2"
+  java -jar class-winter-core-2.8.0.jar k1="v1" k2="v2"
   ```
 
 ## <font  face="幼圆" color = "#3399EA" >加密参数</font>
@@ -120,6 +120,24 @@
     <td>includePrefix=com
     <br/>
 includePrefix=com,org</td>
+  </tr>
+ <tr align="center">
+    <td>cca(依托于includePrefix)</td>
+    <td>否</td>
+    <td>作为includePrefix的附加设置,设置是否清空类上的注释(写法同url后面设置参数)</td>
+    <td>includePrefix=com?cca=true</td>
+  </tr>
+ <tr align="center">
+    <td>cma(依托于includePrefix)</td>
+    <td>否</td>
+    <td>作为includePrefix的附加设置,设置是否清空方法上的注释(写法同url后面设置参数)</td>
+    <td>includePrefix=com?cca=true&cma=true</td>
+  </tr>
+ <tr align="center">
+    <td>cfa(依托于includePrefix)</td>
+    <td>否</td>
+    <td>作为includePrefix的附加设置,设置是否清空字段上的注释(写法同url后面设置参数)</td>
+    <td>includePrefix=com?cca=true&cma=true&cfa=true</td>
   </tr>
     <tr align="center">
     <td>excludePrefix</td>
@@ -270,6 +288,7 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
 
 
 
+
 ## <font  face="幼圆" color = "#3399EA" >解密(启动)</font>
 
 通过-javaagent指定代理进行解密启动。
@@ -277,8 +296,8 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
   ```bash
   # 假设your-project-encrypted.jar是由class-winter加密后的包,那么你可以这么启动
   java -javaagent:/your-project-encrypted.jar -jar /your-project-encrypted.jar
-  # 也可以用class-winter-core-2.7.9.jar
-  # java -javaagent:/class-winter-core-2.7.9.jar -jar /your-project-encrypted.jar
+  # 也可以用class-winter-core-2.8.0.jar
+  # java -javaagent:/class-winter-core-2.8.0.jar -jar /your-project-encrypted.jar
   # 或者指定参数
   # java -javaagent:/your-project-encrypted.jar=debug=true,password=pwd12345 -jar /your-project-encrypted.jar
   # 参数可以引起来(linux)
@@ -294,39 +313,43 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
     编辑tomcat/bin/catalina.sh文件,在最上面加上
 
     ```bash
-    # 如果你有参数, 那么 -javaagent:/class-winter-core-2.7.9.jar=k1=v1,k2=v2
-    CATALINA_OPTS="$CATALINA_OPTS -javaagent:/class-winter-core-2.7.9.jar=debug=true";
+    # 如果你有参数, 那么 -javaagent:/class-winter-core-2.8.0.jar=k1=v1,k2=v2
+    # tomcat启动往往需要通过decryptProjectPathPrefix指定要解密的路径
+    CATALINA_OPTS="$CATALINA_OPTS -javaagent:/class-winter-core-2.8.0.jar=debug=true,decryptProjectPathPrefix=/usr/local/xxx/";
     export CATALINA_OPTS;
     ```
-
+  
   - linux方式二
-
+  
     在tomcat/bin目录下创建setenv.sh文件,并写上
-
+  
     ```bash
-    # 如果你有参数, 那么 -javaagent:/class-winter-core-2.7.9.jar=k1=v1,k2=v2
-    JAVA_OPTS="$JAVA_OPTS -javaagent:/class-winter-core-2.7.9.jar=debug=true";
+    # 如果你有参数, 那么 -javaagent:/class-winter-core-2.8.0.jar=k1=v1,k2=v2
+    # tomcat启动往往需要通过decryptProjectPathPrefix指定要解密的路径
+    JAVA_OPTS="$JAVA_OPTS -javaagent:/class-winter-core-2.8.0.jar=debug=true,decryptProjectPathPrefix=/usr/local/xxx/";
     export JAVA_OPTS;
     ```
 
   - windows方式一
 
     编辑tomcat/bin/catalina.bat文件,在@echo off后加上catalina参数
-
+  
     ```bash
-    rem 如果你有参数, 那么 -javaagent:D:/class-winter-core-2.7.9.jar=k1=v1,k2=v2
-    set CATALINA_OPTS="-javaagent:D:/class-winter-core-2.7.9.jar"
+    rem 如果你有参数, 那么 -javaagent:D:/class-winter-core-2.8.0.jar=k1=v1,k2=v2
+    rem tomcat启动往往需要通过decryptProjectPathPrefix指定要解密的路径
+    set CATALINA_OPTS="-javaagent:D:/class-winter-core-2.8.0.jar=decryptProjectPathPrefix=/D:/tmp/"
     ```
-
+  
   - windows方式二
-
+  
     在tomcat/bin目录下创建setenv.bat文件,并写上
-
+  
     ```bash
-    rem 如果你有参数, 那么 -javaagent:D:/class-winter-core-2.7.9.jar=k1=v1,k2=v2
-    set JAVA_OPTS="-javaagent:D:/class-winter-core-2.7.9.jar"
+    rem 如果你有参数, 那么 -javaagent:D:/class-winter-core-2.8.0.jar=k1=v1,k2=v2
+    rem tomcat启动往往需要通过decryptProjectPathPrefix指定要解密的路径
+    set JAVA_OPTS="-javaagent:D:/class-winter-core-2.8.0.jar=decryptProjectPathPrefix=/D:/tmp/"
     ```
-
+  
 - #### <font  face="幼圆" color = "#86CA5E" >IDE解密(启动)</font>
 
   > 设置`-javaagent`参数指定class-winter-core或者任一加密包。有必要的话,再搭配`skipProjectPathPrefix`或者`decryptProjectPathPrefix`参数启动即可
@@ -365,7 +388,7 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
     <tr align="center">
     <td>skipProjectPathPrefix</td>
     <td>否</td>
-    <td>是否跳过指定前缀的项目路径(当class-winter解密逻辑试图解析那些进入premain但是非class-winter加密项目时,会因为获取印章失败Obtain project seal fail而停止,此时如果确认这个项目没有加密文件的话,可以使用此参数跳过)<br/>注:不知道此值怎么填的,可以把debug代开, 观察日志 Exist projectPath -> xxx 或输出所有的projectPath <br/>注:此参数由2.6.4版本开始支持</td>
+    <td>是否跳过指定前缀的项目路径(当class-winter解密逻辑试图解析那些进入premain但是非class-winter加密项目时,会因为获取印章失败Obtain project seal fail而停止,此时如果确认这个项目没有加密文件的话,可以使用此参数跳过)<br/>注:值中的路径分隔符请统一使用/ <br/>注:不知道此值怎么填的,可以把debug代开, 观察日志 Exist projectPath -> xxx 或输出所有的projectPath <br/>注:此参数由2.6.4版本开始支持</td>
     <td>
         skipProjectPathPrefix=/D:/apache-tomcat-9.0.71/bin/
         <br/>
@@ -375,7 +398,7 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
     <tr align="center">
     <td>decryptProjectPathPrefix</td>
     <td>否</td>
-    <td>是否仅解密指定前缀的项目路径(优先级低于skipProjectPathPrefix)<br/>注:不知道此值怎么填的,可以把debug代开, 观察日志 Exist projectPath -> xxx 或输出所有的projectPath <br/>注:此参数由2.6.6版本开始支持</td>
+    <td>是否仅解密指定前缀的项目路径(优先级低于skipProjectPathPrefix)<br/>注:值中的路径分隔符请统一使用/ <br/>注:不知道此值怎么填的,可以把debug代开, 观察日志 Exist projectPath -> xxx 或输出所有的projectPath <br/>注:此参数由2.6.6版本开始支持</td>
     <td>
         decryptProjectPathPrefix=/D:/apache-tomcat-9.0.71/bin/
         <br/>
@@ -435,7 +458,7 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
       <parent>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-parent</artifactId>
-          <version>2.7.9</version>
+          <version>2.8.0</version>
           <relativePath/> <!-- lookup parent from repository -->
       </parent>
       <groupId>com.example</groupId>
@@ -541,6 +564,15 @@ linux示例:tips='请不要直接使用混淆后的jar/war'</td>
 
 
 
+## 功能扩展案例
+
+> 感谢以下小伙伴们的优秀扩展案例
+
+| 序号   | 扩展功能   | 作者                                                         | 仓库地址                                                    |
+| ------ | ---------- | ------------------------------------------------------------ | ----------------------------------------------------------- |
+| 1      | 机器码支持 | qq号:`85546017`<br /><br />当前(2023-06-11)状态:<br />&emsp;qq名:`╮(╯_╰)╭`<br />&emsp;是否在class-winter QQ群:是 | [点击跳转](https://gitee.com/tts2/class-winter/tree/master) |
+| ...... | ......     | ......                                                       | ......                                                      |
+
 ## QQ群
 
 > 群号:727930548

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

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

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

@@ -24,8 +24,8 @@ public class Forward {
     public static void main(String[] args) {
         /// main test
         ///args =new String[]{
-        ///        "originJarOrWar=C:/Users/Administrator/Desktop/test.war",
-        ///        "includePrefix=com.ideaaedi.test"
+        ///        "originJarOrWar=D:/working/class-winter/class-winter-core/target/test-classes/boot-jar-encrypted.jar",
+        ///        "includePrefix=com?cca=true&cma=true&koa=false&cfa=true,com.ideaaedi,com.ideaaedi.demo"
         ///};
         // 解析参数
         String originJarOrWar = parseValueByPrefixFromTail("originJarOrWar=", args);

+ 59 - 36
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/Reverses.java

@@ -120,8 +120,8 @@ public class Reverses {
                     try {
                         projectPath = JarUtil.getRootPath(projectPath);
                     } catch (ClassWinterException e) {
-                        // 如果没有指定解密该projectPath,那么返回
-                        if (!ifPointToDecrypt(decryptProjectPathPrefixSet, projectPath)) {
+                        // 如果不需要试着解密该projectPath,那么返回
+                        if (notPointToDecrypt(projectPath, decryptProjectPathPrefixSet)) {
                             return classfileBuffer;
                         }
                     } catch (Exception e) {
@@ -141,37 +141,16 @@ public class Reverses {
                             return classfileBuffer;
                         }
                     }
-                    if (decryptProjectPathPrefixSet.size() > 0) {
-                        if (!ifPointToDecrypt(decryptProjectPathPrefixSet, projectPath)) {
+                    boolean settingDecryptProjectPath = decryptProjectPathPrefixSet.size() > 0;
+                    if (settingDecryptProjectPath) {
+                        if (notPointToDecrypt(projectPath, decryptProjectPathPrefixSet)) {
                             return classfileBuffer;
                         }
                     }
                     className = className.replace("/", ".").replace("\\", ".");
-                    // 本项目的印章
-                    if (Cache.sealCache == null) {
-                        try {
-                            byte[] sealByte = IOUtil.readFileFromWorkbenchRoot(new File(projectPath), Constant.SEAL_FILE);
-                            if (sealByte == null) {
-                                Logger.error(Reverses.class, "Obtain project seal fail. "
-                                        + "Maybe you need to try it with decrypt params 'decryptProjectPathPrefix' or 'skipProjectPathPrefix'. "
-                                        + "@see https://gitee.com/JustryDeng/class-winter#%E8%A7%A3%E5%AF%86%E5%8F%82%E6%95%B0 ");
-                                // 结束程序
-                                exit(projectPath);
-                            }
-                            String sealContent = new String(sealByte, StandardCharsets.UTF_8);
-                            Logger.debug(Reverses.class, "seal of the project is -> " + sealContent);
-                            Cache.sealCache = sealContent;
-                        } catch (Exception e) {
-                            Logger.error(Reverses.class, "Obtain project seal fail.");
-                            Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
-                            exit(projectPath);
-                            for (int i = 0; i < Constant.TEN; i++) {
-                                Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
-                            }
-                            return null;
-                        }
-                    }
-                    
+                    // 抽取印章
+                    extractSeal(projectPath, settingDecryptProjectPath);
+    
                     /// ============================================ 1.校验启动时是否输入了加密时指定的jvm参数   2.处理non-class文件
                     final String inputPwd = javaagentCmdArgs.getPassword();
                     if (firstExec.compareAndSet(false, true)) {
@@ -251,7 +230,7 @@ public class Reverses {
                     
                     /// ============================================ 处理class文件
                     // 判断是否应该解密
-                    if (DecryptExecutor.checklistContain(projectPath, className) && DecryptExecutor.verifySeal(classfileBuffer)) {
+                    if (DecryptExecutor.checklistContain(projectPath, className) && DecryptExecutor.verifySeal(projectPath, classfileBuffer)) {
                         //noinspection DuplicatedCode
                         try {
                             Logger.debug(Reverses.class, "Decrypt class[" + className + "] start.");
@@ -304,17 +283,61 @@ public class Reverses {
     }
     
     /**
-     * 是否指定了要试着解密projectPath指向的代码
+     * 抽取项目印章
+     *
+     * @param settingDecryptProjectPath 是否指定了项目解密路径前缀
+     */
+    private static void extractSeal(String projectPath, boolean settingDecryptProjectPath) {
+        if (Cache.sealCache.containsKey(projectPath)) {
+            return;
+        }
+        try {
+            byte[] sealByte = IOUtil.readFileFromWorkbenchRoot(new File(projectPath), Constant.SEAL_FILE);
+            String sealContent = null;
+            if (sealByte == null) {
+                // 在没有指定解密路径的情况下,没有获取到印章的话,那么获取第一个印章作为本projectPath的印章
+                if (!settingDecryptProjectPath && Cache.firstSealCache != null) {
+                    sealContent = Cache.firstSealCache;
+                    Logger.debug(Reverses.class, "Use first-seal as curr project seal.");
+                }
+            } else {
+                sealContent = new String(sealByte, StandardCharsets.UTF_8);
+                Logger.debug(Reverses.class, "direct-seal of the project is -> " + sealContent);
+            }
+            if (sealContent == null) {
+                Logger.error(Reverses.class, "Obtain project seal fail.");
+                // 结束程序
+                exit(projectPath);
+            }
+            Cache.sealCache.put(projectPath, sealContent);
+            if (Cache.firstSealCache == null) {
+                Cache.firstSealCache = sealContent;
+                Cache.firstSealProjectPath = projectPath;
+                Logger.debug(Reverses.class, "first-seal found. projectPath -> " + Cache.firstSealProjectPath);
+                Logger.debug(Reverses.class, "first-seal found. sealContent -> " + Cache.firstSealCache);
+            }
+        } catch (Exception e) {
+            Logger.error(Reverses.class, "Obtain project seal fail.");
+            Logger.error(Reverses.class, ExceptionUtil.getStackTraceMessage(e));
+            exit(projectPath);
+            for (int i = 0; i < Constant.TEN; i++) {
+                Logger.error(Reverses.class, "!!!!!!!!!!! class-winter Invalidation. !!!!!!!!!!!");
+            }
+        }
+    }
+    
+    /**
+     * 是否不需要试着解密projectPath指向的代码
      */
-    private static boolean ifPointToDecrypt(Set<String> decryptProjectPathPrefixSet, String projectPath) {
-        boolean needTryDecrypt = false;
+    private static boolean notPointToDecrypt(String projectPath, Set<String> decryptProjectPathPrefixSet) {
+        boolean notNeedTryDecrypt = true;
         for (String decryptProjectPathPrefix : decryptProjectPathPrefixSet) {
             if (projectPath != null && projectPath.startsWith(decryptProjectPathPrefix)) {
-                needTryDecrypt = true;
+                notNeedTryDecrypt = false;
                 break;
             }
         }
-        return needTryDecrypt;
+        return notNeedTryDecrypt;
     }
     
     /**
@@ -326,7 +349,7 @@ public class Reverses {
                 Logger.error(Reverses.class, "Curr projectPath is -> " + projectPath);
             }
             TimeUnit.SECONDS.sleep(10);
-        } catch (InterruptedException interruptedException) {
+        } catch (InterruptedException ignore) {
         }
         System.exit(-1);
     }

+ 24 - 19
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/DecryptExecutor.java

@@ -20,6 +20,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 import java.util.zip.ZipFile;
 
@@ -31,14 +32,15 @@ import java.util.zip.ZipFile;
  */
 public class DecryptExecutor {
     
-    /** 要解密的文件清单,记录已加密的class的全类名的清单文件 */
-    private static Set<String> checklist;
+    /** key- projectPath; value-要解密的文件清单,记录已加密的class的全类名的清单文件 */
+    private static final Map<String, Set<String>> checklist = new ConcurrentHashMap<>(8);
     
     /**
-     * 以key-value的形式记录lib中的被混淆的类(key-被混淆的类的无后缀全类名, value-存放采集到的类所在lib的信息的文件夹)
-     *
+     * 以key-value的形式记录lib中的被混淆的类(key-被混淆的类的无后缀全类名, value-存放采集到的类所在lib的信息的文件夹)
+     * <pre>
      * key   - 形如: com.abc.xyz.Qwer
      * value - 形如: META-INF/winter/abc-1.0.0_jar/
+     * </pre>
      */
     private static Map<String, String> checklistOfAllLibsMap;
 
@@ -99,19 +101,20 @@ public class DecryptExecutor {
      * @return classLongName是否在(记录已加密类的)清单列表中
      */
     public static boolean checklistContain(String projectPath, String classLongName) {
-        if (checklist == null) {
+        // 如果当前projectPath的印章,用的是Cache.firstSealCache的话,那么此方法中获取checklist,也获取Cache.firstSealCache对应的checklist
+        projectPath = Cache.firstSealCache.equals(Cache.sealCache.get(projectPath)) ? Cache.firstSealProjectPath : projectPath;
+        if (!checklist.containsKey(projectPath)) {
             byte[] checklistByte = IOUtil.readFileFromWorkbenchRoot(new File(projectPath),
                     Constant.ALREADY_ENCRYPTED_CLASS_CHECKLIST_CLASSES_SAVE_FILE);
             if (checklistByte == null) {
-                checklist = new HashSet<>();
                 Logger.debug(DecryptExecutor.class, "checklistByte is null. ");
             } else {
                 String checklistContent = new String(checklistByte, StandardCharsets.UTF_8);
                 Logger.debug(DecryptExecutor.class, "checklistContent " + checklistContent);
-                checklist = new HashSet<>(Arrays.asList(checklistContent.split(Constant.COMMA)));
+                checklist.put(projectPath, new HashSet<>(Arrays.asList(checklistContent.split(Constant.COMMA))));
             }
         }
-        return checklist.contains(classLongName);
+        return checklist.get(projectPath).contains(classLongName);
     }
     
     /**
@@ -175,12 +178,16 @@ public class DecryptExecutor {
     /**
      * 判断类classBytes是否被盖章
      *
-     * @param classBytes
-     *            类的字节码
+     * @param projectPath 项目路径
+     * @param classBytes 类的字节码
      * @return classBytes中是否存在加密印章
      */
-    public static boolean verifySeal(byte[] classBytes) {
-        return new String(classBytes, StandardCharsets.UTF_8).contains(Cache.sealCache);
+    public static boolean verifySeal(String projectPath, byte[] classBytes) {
+        String seal = Cache.sealCache.get(projectPath);
+        if (StrUtil.isBlank(seal)) {
+            throw new ClassWinterException("seal should not be blank. curr projectPath -> " + projectPath + ". curr sealCache is -> " + Cache.sealCache);
+        }
+        return new String(classBytes, StandardCharsets.UTF_8).contains(seal);
     }
     
     /**
@@ -263,10 +270,8 @@ public class DecryptExecutor {
     /**
      * 恢复非class文件的混淆
      *
-     * @param zipFilePath
-     *            项目jar/war包路径
-     * @param password
-     *            项目加密密码(可能为null)
+     * @param zipFilePath 项目jar/war包路径
+     * @param password 项目加密密码(可能为null)
      * @return  被替换了的ZipEntry的相对路径及重写前后的内容信息<br/>
      *          k - ZipEntry的相对路径,如:BOOT-INF/classes/application.yml<br/>
      *          v - 左:重写前的内容,右:重写后的内容
@@ -291,7 +296,7 @@ public class DecryptExecutor {
         for (String zipEntryName : needToDecryptNonClassFileSet) {
             // 只处理有印章的
             byte[] cleanedBytes = IOUtil.readFileFromWorkbenchRoot(zipFile, zipEntryName);
-            if (DecryptExecutor.verifySeal(cleanedBytes)) {
+            if (DecryptExecutor.verifySeal(zipFilePath, cleanedBytes)) {
                 byte[] encryptedBytes = IOUtil.readFileFromWorkbenchRoot(zipFile,
                         Constant.DEFAULT_ENCRYPTED_NON_CLASSES_SAVE_DIR + zipEntryName);
                 byte[] bytes = EncryptUtil.decrypt(encryptedBytes, userIfInputPwdWhileDecrypt ? password :
@@ -329,7 +334,7 @@ public class DecryptExecutor {
             }
             // 存时,是加密存进去的; 这里读取时,解密一下
             passwordByte = EncryptUtil.decrypt(new String(passwordByte, StandardCharsets.UTF_8),
-                    Cache.sealCache.toCharArray()).getBytes(StandardCharsets.UTF_8);
+                    Cache.sealCache.get(projectPath).toCharArray()).getBytes(StandardCharsets.UTF_8);
             String password = new String(passwordByte, StandardCharsets.UTF_8);
             char[] passwordCharArr = password.toCharArray();
             // 放入缓存
@@ -348,7 +353,7 @@ public class DecryptExecutor {
             Objects.requireNonNull(passwordByte);
             // 存时,是加密存进去的; 这里读取时,解密一下
             char[] passwordCharArr = EncryptUtil.decrypt(new String(passwordByte, StandardCharsets.UTF_8),
-                    Cache.sealCache.toCharArray()).toCharArray();
+                    Cache.sealCache.get(projectPath).toCharArray()).toCharArray();
             Cache.libPasswordCache.put(classWinterInfoDir, passwordCharArr);
             return passwordCharArr;
         }

+ 106 - 30
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/executor/EncryptExecutor.java

@@ -16,6 +16,7 @@ import winter.com.ideaaedi.classwinter.author.JustryDeng;
 import winter.com.ideaaedi.classwinter.exception.ClassWinterException;
 import winter.com.ideaaedi.classwinter.util.Cache;
 import winter.com.ideaaedi.classwinter.util.Constant;
+import winter.com.ideaaedi.classwinter.util.EncryptClassArgs;
 import winter.com.ideaaedi.classwinter.util.EncryptUtil;
 import winter.com.ideaaedi.classwinter.util.ExceptionUtil;
 import winter.com.ideaaedi.classwinter.util.FileOrderSupport;
@@ -40,11 +41,15 @@ import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Base64;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -81,6 +86,7 @@ public class EncryptExecutor {
         CLASS_WINTER_FILES.add(PathUtil.class.getName().replace(".", "/") + Constant.CLASS_SUFFIX);
         CLASS_WINTER_FILES.add(SimpleFileOrderSupport.class.getName().replace(".", "/") + Constant.CLASS_SUFFIX);
         CLASS_WINTER_FILES.add(StrUtil.class.getName().replace(".", "/") + Constant.CLASS_SUFFIX);
+        CLASS_WINTER_FILES.add(EncryptClassArgs.class.getName().replace(".", "/") + Constant.CLASS_SUFFIX);
         CLASS_WINTER_FILES.add(Reverses.class.getName().replace(".", "/") + Constant.CLASS_SUFFIX);
         CLASS_WINTER_FILES.add(Reverses.class.getName().replace(".", "/").replace("Reverses", "Reverses$1") + Constant.CLASS_SUFFIX);
     }
@@ -125,8 +131,10 @@ public class EncryptExecutor {
     
     /**
      * 要加密的类的全类名前缀(也可以精确匹配)
+     * <br />
+     * 注:可携带参数, 写法同url后面待参数 url?key1=value1&key2=value2
      */
-    private final Set<String> includePrefixSet;
+    private final LinkedHashSet<String> includePrefixSet;
     
     /**
      * 要加密的lib包(若不指定,则加密筛选范围默认仅从项目代码本身进行筛选加密)。
@@ -164,7 +172,7 @@ public class EncryptExecutor {
      *  在一些外置lib的项目中,可能需要用到此参数;如果是内置lib,忽略此参数即可
      * </pre>
      */
-    private String alreadyProtectedRootDir;
+    private final String alreadyProtectedRootDir;
     
     /**
      * 已经被class-winter加密过了的lib的信息
@@ -215,7 +223,7 @@ public class EncryptExecutor {
     private EncryptExecutor(String originJarOrWar, boolean originIsJar, String finalName, String targetRootDir,
                             String targetLibDir, String targetClassesDir, String password,
                             Set<String> includeXmlPrefixSet, Set<String> excludeXmlPrefixSet, Set<String> toCleanXmlChildElementNameSet,
-                            Set<String> includePrefixSet, Set<String> excludePrefixSet, Set<String> includeLibSet,
+                            LinkedHashSet<String> includePrefixSet, Set<String> excludePrefixSet, Set<String> includeLibSet,
                             String alreadyProtectedRootDir, Set<Pair<String, String>> alreadyProtectedLibSet,
                             String supportFile, Set<String> jvmArgCheckSet) {
         this.originJarOrWar = originJarOrWar;
@@ -264,12 +272,12 @@ public class EncryptExecutor {
         
         // step3. 找到需要加密混淆的class文件
         List<File> allFiles = IOUtil.listSubFile(targetRootDirFile, 1);
-        List<File> allNeedEncryptedClassFileList = filterClasses(allFiles);
+        List<EncryptClassArgs> allNeedEncryptedClassInfoList = filterClasses(allFiles);
         showProcess("step03", lastStep, "filter-classes");
         
         // step4.1. 根据原.class文件,生成加密后的.class文件,并存至Constant.ENCRYPTED_CLASSES_SAVE_DIR
-        List<String> allAlreadyEncryptedClassFileList = encryptClasses(allNeedEncryptedClassFileList, new File(targetRootDir,
-                Constant.DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR));
+        List<String> allAlreadyEncryptedClassFileList = encryptClasses(allNeedEncryptedClassInfoList,
+                new File(targetRootDir, Constant.DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR));
         // step4.2. 生成记录已加密class的全类名的清单文件
         generateChecklistFile(allAlreadyEncryptedClassFileList);
         showProcess("step04", lastStep, "encrypt-classes");
@@ -283,7 +291,7 @@ public class EncryptExecutor {
         showProcess("step06", lastStep, "setting-jvm-arg-check-items");
 
         // step7. 混淆原.class文件(即:清空方法体)
-        clearClassMethod(allNeedEncryptedClassFileList);
+        clearClassMethod(allNeedEncryptedClassInfoList);
         showProcess("step07", lastStep, "clear-class-method");
     
         // step8. 清除META-INF/maven下pom.xml中关于class-winter-plugin的相关信息
@@ -368,6 +376,9 @@ public class EncryptExecutor {
         // 清空原xml文件的关键节点
         for (String zipEntryName : encryptNonClassFileSet) {
             byte[] bytes = IOUtil.readFileFromWorkbenchRoot(targetRootDirFile, zipEntryName);
+            if (bytes == null) {
+                continue;
+            }
             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));
         }
@@ -398,9 +409,7 @@ public class EncryptExecutor {
      *
      * <?xml version="1.0" encoding="UTF-8"?>
      * <project ...>
-     *
      *     ......
-     *
      *     <build>
      *         <plugins>
      *         	// 要把这部分清除 start
@@ -757,10 +766,9 @@ public class EncryptExecutor {
     /**
      * 清空class文件的方法体,并保留参数信息
      *
-     * @param classFiles
-     *            需要清空方法体的class文件
+     * @param allNeedEncryptedClassInfoList 需要加密的class相关参数集合
      */
-    private void clearClassMethod(List<File> classFiles) {
+    private void clearClassMethod(List<EncryptClassArgs> allNeedEncryptedClassInfoList) {
         // step0. 初始化javassist
         ClassPool pool = ClassPool.getDefault();
         // step1. 把所有可能涉及到的jar、.class添加进加载路径
@@ -774,10 +782,11 @@ public class EncryptExecutor {
             throw new ClassWinterException(e);
         }
         // step2. 修改class方法体,并保存文件
-        classFiles.forEach(classFile -> {
+        allNeedEncryptedClassInfoList.forEach(encryptClassArgs -> {
+            File classFile = encryptClassArgs.getEncryptClassFile();
             String className = JavassistUtil.resolveClassName(classFile.getAbsolutePath(), true);
             try {
-                byte[] bts = JavassistUtil.clearMethodBody(pool, className, true);
+                byte[] bts = JavassistUtil.clearMethodBody(pool, className, encryptClassArgs);
                 IOUtil.toFile(bts, classFile, true);
             } catch (NotFoundException e) {
                 // ignore
@@ -865,13 +874,12 @@ public class EncryptExecutor {
     /**
      * 加密class文件,并将加密后的class文件放在savaDir里
      *
-     * @param classFiles
-     *            需要加密的class文件
-     * @param savaDir
-     *            加密后的class文件的存储目录
+     * @param encryptClassInfoList 需要加密的class相关参数集合
+     * @param savaDir 加密后的class文件的存储目录
      * @return  已经加密了的类的全类名(如: com.aaa.bbb.Abc)
      */
-    private List<String> encryptClasses(List<File> classFiles, File savaDir) {
+    private List<String> encryptClasses(List<EncryptClassArgs> encryptClassInfoList, File savaDir) {
+        List<File> classFiles = encryptClassInfoList.stream().map(EncryptClassArgs::getEncryptClassFile).collect(Collectors.toList());
         // 保证目录存在
         JarUtil.guarantyDirExist(savaDir);
         classFiles.stream().map(File::getName).forEach(name -> {
@@ -924,35 +932,81 @@ public class EncryptExecutor {
     /**
      * 找出所有需要加密的class文件
      *
-     * @param allFileList
-     *            所有文件
-     * @return  需要加密的class文件
+     * @param allFileList 所有文件
+     * @return  需要加密的class信息
      */
-    private List<File> filterClasses(List<File> allFileList) {
+    private List<EncryptClassArgs> filterClasses(List<File> allFileList) {
         return allFileList.stream()
                 .filter(file -> file.getName().endsWith(Constant.CLASS_SUFFIX))
-                .filter(file -> {
+                .map(file -> {
                     // 全类名
                     String classLongName = JavassistUtil.resolveClassName(file.getAbsolutePath(), true);
                     if (excludePrefixSet != null) {
                         for (String excludePrefix : excludePrefixSet) {
                             if (classLongName.startsWith(excludePrefix)) {
                                 // 排除
-                                return false;
+                                return null;
                             }
                         }
         
                     }
                     if (includePrefixSet != null) {
                         for (String includePrefix : includePrefixSet) {
-                            if (classLongName.startsWith(includePrefix)) {
+                            Pair<String, Map<String, String>> pair = extraPrefixAndParam(includePrefix);
+                            String pureIncludePrefix = pair.getLeft();
+                            Map<String, String> params = pair.getRight();
+                            
+                            if (classLongName.startsWith(pureIncludePrefix)) {
                                 // 包含
-                                return true;
+                                return EncryptClassArgs.create(file,
+                                        StrUtil.isTrueDefault(params.get(Constant.CLEAN_CLASS_ANNOTATION_PARAM_NAME), false),
+                                        StrUtil.isTrueDefault(params.get(Constant.CLEAN_METHOD_ANNOTATION_PARAM_NAME), false),
+                                        StrUtil.isTrueDefault(params.get(Constant.CLEAN_FIELD_ANNOTATION_PARAM_NAME), false)
+                                );
                             }
                         }
                     }
-                    return false;
-                }).collect(Collectors.toList());
+                    return null;
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+    
+    /**
+     * 从includePrefix中抽取纯净的前缀及相关参数
+     * <br />
+     * 注:参考从url中获取参数
+     *
+     * @param includePrefix 要加密的类前缀
+     *                      <p>
+     *                      可携带参数,如:com.ideaaedi.demo.service?key1=value1&key2=value2
+     *
+     * @return <ul>
+     *     <li>左-要加密的类前缀</li>
+     *     <li>右-相关加密参数信息</li>
+     * </ul>
+     */
+    private Pair<String, Map<String, String>> extraPrefixAndParam(String includePrefix) {
+        Map<String, String> param = new HashMap<>(8);
+        int index = includePrefix.indexOf("?");
+        if (index < 0) {
+           return Pair.of(includePrefix, param);
+        }
+        String pureIncludePrefix = includePrefix.substring(0, index);
+        String paramStr = includePrefix.substring(index + 1);
+        for (String keyValue : paramStr.split("&")) {
+            int equalIndex = keyValue.indexOf("=");
+            if (equalIndex < 0) {
+                continue;
+            }
+            String key = keyValue.substring(0, equalIndex).trim();
+            String value = keyValue.substring(equalIndex + 1).trim();
+            if (StrUtil.isBlank(key) || StrUtil.isBlank(value)) {
+                continue;
+            }
+            param.put(key, value);
+        }
+        return Pair.of(pureIncludePrefix, param);
     }
     
     /**
@@ -1222,6 +1276,8 @@ public class EncryptExecutor {
                     Constant.WEB_INF, Constant.CLASSES);
 
             Set<String> includePrefixSet = StrUtil.strToSet(includePrefix);
+            LinkedHashSet<String> includePrefixSortSet = sortByPurePrefixLenthDesc(includePrefixSet);
+    
             Set<String> excludePrefixSet = StrUtil.strToSet(excludePrefix);
             Set<String> includeLibSet = StrUtil.strToSet(includeLibs);
             Set<Pair<String, String>> alreadyProtectedLibSet = parseAlreadyProtectedLibs();
@@ -1250,10 +1306,30 @@ public class EncryptExecutor {
             Set<String> toCleanXmlChildElementNameSet = StrUtil.strToSet(toCleanXmlChildElementName);
             
             return new EncryptExecutor(this.originJarOrWar, originIsJar, this.finalName, targetRootDir, targetLibDir, targetClassesDir,
-                    password, includeXmlPrefixSet, excludeXmlPrefixSet, toCleanXmlChildElementNameSet, includePrefixSet,
+                    password, includeXmlPrefixSet, excludeXmlPrefixSet, toCleanXmlChildElementNameSet, includePrefixSortSet,
                     excludePrefixSet, includeLibSet, alreadyProtectedRootDir, alreadyProtectedLibSet, this.supportFile, jvmArgCheckSet);
         }
     
+        /**
+         * 按照(不带参数的)前缀的长度倒序
+         *
+         * @param includePrefixSet 前缀集合
+         *
+         * @return  倒序的前缀集合
+         */
+        private static LinkedHashSet<String> sortByPurePrefixLenthDesc(Set<String> includePrefixSet) {
+            List<String> includePrefixSortList = new ArrayList<>(includePrefixSet);
+            includePrefixSortList.sort(Comparator.comparingInt(x -> {
+                int idx = x.indexOf("?");
+                if (idx < 0) {
+                    return x.length();
+                }
+                return x.substring(0, idx).length();
+            }));
+            Collections.reverse(includePrefixSortList);
+            return new LinkedHashSet<>(includePrefixSortList);
+        }
+    
         /**
          * 解析alreadyProtectedLibs信息为Set<Pair<String, String>>
          */

+ 10 - 4
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/Cache.java

@@ -3,8 +3,8 @@ package winter.com.ideaaedi.classwinter.util;
 import winter.com.ideaaedi.classwinter.author.JustryDeng;
 import winter.com.ideaaedi.classwinter.executor.EncryptExecutor;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * 简单缓存
@@ -24,10 +24,16 @@ public final class Cache {
      *     <li>value-密码</li>
      * </ul>
      */
-    public static Map<String, char[]> passwordCacheForEncrypt = new HashMap<>(8);
+    public static volatile Map<String, char[]> passwordCacheForEncrypt = new ConcurrentHashMap<>(8);
     
-    /** 本项目加密时的印章 */
-    public static volatile String sealCache = null;
+    /** 本项目加密时的印章 (key-projectPath; value-印章) */
+    public static volatile Map<String, String>  sealCache =  new ConcurrentHashMap<>(8);
+    
+    /** 本项目获取到的第一个印章 */
+    public static volatile String  firstSealCache;
+    
+    /** 本项目获取到的第一个印章的项目路径 */
+    public static volatile String  firstSealProjectPath;
     
     /**
      * 以key-value的形式记录lib的印章。(key-存放采集到的类所在lib的信息的文件夹, value-印章字符串)

+ 21 - 0
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/Constant.java

@@ -234,4 +234,25 @@ public interface Constant {
      * NotFoundException标识
      */
     String NOT_FOUND_EXCEPTION_FLAG = "javassist.NotFoundException:";
+    
+    /**
+     * 参数名:是否清除类上的注解. 值为boolean类型
+     * <p>
+     * {@link EncryptClassArgs#isCleanAnnotationOverClazz()}
+     */
+    String CLEAN_CLASS_ANNOTATION_PARAM_NAME = "cca";
+    
+    /**
+     * 参数名:是否清除方法的注解. 值应为boolean类型
+     * <p>
+     * {@link EncryptClassArgs#isCleanAnnotationOverMethod()}
+     */
+    String CLEAN_METHOD_ANNOTATION_PARAM_NAME = "cma";
+    
+    /**
+     * 参数名:是否清除字段的注解. 值应为boolean类型
+     * <p>
+     * {@link EncryptClassArgs#isCleanAnnotationOverField()}
+     */
+    String CLEAN_FIELD_ANNOTATION_PARAM_NAME = "cfa";
 }

+ 104 - 0
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/EncryptClassArgs.java

@@ -0,0 +1,104 @@
+package winter.com.ideaaedi.classwinter.util;
+
+import java.io.File;
+import java.util.Objects;
+
+/**
+ * 加密类相关参数
+ *
+ * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
+ * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
+ * @since 2.8.0
+ */
+public class EncryptClassArgs {
+    
+    /**
+     * 待加密的class文件
+     */
+    private File encryptClassFile;
+    
+    /**
+     * 是否清除类上的注解
+     */
+    private boolean cleanAnnotationOverClazz = false;
+    
+    /**
+     * 是否清除方法的注解
+     */
+    private boolean cleanAnnotationOverMethod = false;
+    
+    /**
+     * 是否清除字段的注解
+     */
+    private boolean cleanAnnotationOverField = false;
+    
+    /**
+     * 是否保留原方法的参数名
+     */
+    private boolean keepOriginArgsName = true;
+    
+    /**
+     * fast create
+     */
+    public static EncryptClassArgs create(File encryptClassFile, boolean cleanAnnotationOverClazz,
+                                          boolean cleanAnnotationOverMethod, boolean cleanAnnotationOverField) {
+        Objects.requireNonNull(encryptClassFile, "encryptClassFile cannot be null.");
+        EncryptClassArgs encryptClassArgs = new EncryptClassArgs();
+        encryptClassArgs.setEncryptClassFile(encryptClassFile);
+        encryptClassArgs.setCleanAnnotationOverClazz(cleanAnnotationOverClazz);
+        encryptClassArgs.setCleanAnnotationOverMethod(cleanAnnotationOverMethod);
+        encryptClassArgs.setCleanAnnotationOverField(cleanAnnotationOverField);
+        return encryptClassArgs;
+    }
+    
+    public File getEncryptClassFile() {
+        return encryptClassFile;
+    }
+    
+    public void setEncryptClassFile(File encryptClassFile) {
+        this.encryptClassFile = encryptClassFile;
+    }
+    
+    public boolean isCleanAnnotationOverClazz() {
+        return cleanAnnotationOverClazz;
+    }
+    
+    public void setCleanAnnotationOverClazz(boolean cleanAnnotationOverClazz) {
+        this.cleanAnnotationOverClazz = cleanAnnotationOverClazz;
+    }
+    
+    public boolean isCleanAnnotationOverMethod() {
+        return cleanAnnotationOverMethod;
+    }
+    
+    public void setCleanAnnotationOverMethod(boolean cleanAnnotationOverMethod) {
+        this.cleanAnnotationOverMethod = cleanAnnotationOverMethod;
+    }
+    
+    public boolean isCleanAnnotationOverField() {
+        return cleanAnnotationOverField;
+    }
+    
+    public void setCleanAnnotationOverField(boolean cleanAnnotationOverField) {
+        this.cleanAnnotationOverField = cleanAnnotationOverField;
+    }
+    
+    public boolean isKeepOriginArgsName() {
+        return keepOriginArgsName;
+    }
+    
+    public void setKeepOriginArgsName(boolean keepOriginArgsName) {
+        this.keepOriginArgsName = keepOriginArgsName;
+    }
+    
+    @Override
+    public String toString() {
+        return "EncryptClassArgs{" +
+                "encryptClassFile=" + encryptClassFile +
+                ", cleanAnnotationOverClazz=" + cleanAnnotationOverClazz +
+                ", cleanAnnotationOverMethod=" + cleanAnnotationOverMethod +
+                ", cleanAnnotationOverField=" + cleanAnnotationOverField +
+                ", keepOriginArgsName=" + keepOriginArgsName +
+                '}';
+    }
+}

+ 73 - 16
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/JavassistUtil.java

@@ -3,8 +3,11 @@ package winter.com.ideaaedi.classwinter.util;
 import javassist.CannotCompileException;
 import javassist.ClassPool;
 import javassist.CtClass;
+import javassist.CtField;
 import javassist.CtMethod;
 import javassist.NotFoundException;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.AttributeInfo;
 import javassist.bytecode.BadBytecode;
 import javassist.bytecode.Bytecode;
 import javassist.bytecode.CodeAttribute;
@@ -32,12 +35,9 @@ public final class JavassistUtil {
     /**
      * 清空类中的方法体,并简单处理一下main,使提示密码无效
      *
-     * @param classPool
-     *            javassist的classPool
-     * @param className
-     *            要修改的class类的全类名
-     * @param keepOriginArgsName
-     *            是否保留原方法的参数名
+     * @param classPool javassist的classPool
+     * @param className 要修改的class类的全类名
+     * @param encryptClassArgs 加密相关参数
      *
      * @return  处理后的类的字节
      * @throws NotFoundException 当清空的类中涉及到了某些其他的类,但是根本就没有引入(这些其他的类)相应的依赖时,就会抛出此异常(即:这个类本身就报错来着)
@@ -46,7 +46,8 @@ public final class JavassistUtil {
      *                           注:在某些其它情况下,也会抛出此异常。
      * @throws  CannotCompileException 统一封装异常
      */
-    public static byte[] clearMethodBody(ClassPool classPool, String className, boolean keepOriginArgsName) throws CannotCompileException, NotFoundException {
+    public static byte[] clearMethodBody(ClassPool classPool, String className, EncryptClassArgs encryptClassArgs)
+            throws CannotCompileException, NotFoundException {
         CtClass ctClass;
         try {
             ctClass = classPool.getCtClass(className);
@@ -54,6 +55,19 @@ public final class JavassistUtil {
                 Logger.debug(JavassistUtil.class, "defrost class " + className);
                 ctClass.defrost();
             }
+            // 清空注解
+            boolean cleanOverCLass = encryptClassArgs.isCleanAnnotationOverClazz();
+            boolean cleanOverMethod = encryptClassArgs.isCleanAnnotationOverMethod();
+            boolean cleanOverField = encryptClassArgs.isCleanAnnotationOverField();
+            if (cleanOverCLass || cleanOverMethod || cleanOverField) {
+                try {
+                    clearAnnotation(ctClass, cleanOverCLass, cleanOverMethod, cleanOverField);
+                } catch (Exception ignore) {
+                    Logger.warn(JavassistUtil.class, "clean className [" + className + "] annotation fail. ignore.");
+                    // ignore
+                }
+            }
+            boolean keepOriginArgsName = encryptClassArgs.isKeepOriginArgsName();
             CtMethod[] methods = ctClass.getDeclaredMethods();
             for (CtMethod ctMethod : methods) {
                 // 当前类中的方法 && 非构造方法
@@ -67,18 +81,12 @@ public final class JavassistUtil {
                     } else {
                         ctMethod.setBody(null);
                     }
-                    /// 顺带处理一下main方法:提示jar包已经被保护,请使用javaagent启动项目
-                    /// if (isMain(ctMethod)) {
-                    ///     ctMethod.insertBefore(
-                    ///             "System.out.println(\"\\n " + TIPS +" \\n\");"
-                    ///                     + "\nSystem.exit(-1);"
-                    ///     );
-                    /// }
                     try {
                         ctMethod.insertBefore(
-                                "System.out.println(\"\\n " + Constant.TIPS +" \\n\");"
+                                "System.out.println(\"\\n " + Constant.TIPS + " \\n\");"
                                 // 也输出印章信息吧
-                                + "\nSystem.out.println(\" " + Constant.SEAL +" \");"
+                                + "\nSystem.out.println(\" Located in '" + className + "' \");"
+                                + "\nSystem.out.println(\" " + Constant.SEAL + " \");"
                                 + "\nSystem.exit(-1);"
                         );
                     } catch (CannotCompileException e) {
@@ -97,6 +105,55 @@ public final class JavassistUtil {
         }
     }
     
+    /**
+     * 清空注解
+     *
+     * @param ctClass 目标类
+     * @param cleanOverCLass 是否清空类上的注解
+     * @param cleanOverMethod 是否清空方法上的注解
+     * @param cleanOverField 是否清空字段上的注解
+     */
+    private static void clearAnnotation(CtClass ctClass, boolean cleanOverCLass, boolean cleanOverMethod, boolean cleanOverField) {
+        // 清空类上的注解
+        if (cleanOverCLass) {
+            removeAnnotationsAttribute(ctClass.getClassFile().getAttributes());
+        }
+        // 清空方法上的注解
+        if (cleanOverMethod) {
+            CtMethod[] methods = ctClass.getDeclaredMethods();
+            for (CtMethod ctMethod : methods) {
+                removeAnnotationsAttribute(ctMethod.getMethodInfo().getAttributes());
+            }
+        }
+        // 清空字段上的注解
+        if (cleanOverField) {
+            CtField[] fields = ctClass.getDeclaredFields();
+            for (CtField declaredField : fields) {
+                removeAnnotationsAttribute(declaredField.getFieldInfo().getAttributes());
+            }
+        }
+    }
+    
+    /**
+     * 移除注解属性
+     *
+     * @param attributeInfoList 属性信息集合
+     * @return 操作是否成功
+     */
+    @SuppressWarnings("UnusedReturnValue")
+    private static boolean removeAnnotationsAttribute(List<AttributeInfo> attributeInfoList) {
+        if (attributeInfoList == null || attributeInfoList.size() == 0) {
+            return true;
+        }
+        return attributeInfoList.removeIf(x -> {
+            try {
+                return x instanceof AnnotationsAttribute;
+            } catch (Exception e) {
+                return false;
+            }
+        });
+    }
+    
     /**
      * 加载paths指定的.jar文件(或加载对应子孙目录下的所有jar包)
      *

+ 14 - 0
class-winter-core/src/main/java/winter/com/ideaaedi/classwinter/util/StrUtil.java

@@ -28,6 +28,20 @@ public final class StrUtil {
         return str == null || str.length() == 0;
     }
     
+    /**
+     * 判断字符串是否为true
+     *
+     * @param str 字符串
+     * @param defaultValue 字符串为空时的默认值
+     * @return  是否为true
+     */
+    public static boolean isTrueDefault(String str, boolean defaultValue) {
+        if (isBlank(str)) {
+            return defaultValue;
+        }
+        return str.equalsIgnoreCase("true");
+    }
+    
     /**
      * <p>Checks if a CharSequence is empty (""), null or whitespace only.</p>
      *

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

@@ -68,7 +68,7 @@ public class AllTest {
         // 加密
         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 includePrefix = "com.szlaozicl?cca=true&cfa=true,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 password = "pwd12345";
@@ -77,7 +77,7 @@ public class AllTest {
         String alreadyProtectedLibs = "encrypted-lib-have-pwd-1.0.0.jar:qwer123~";
         String jvmArgCheck = "-XX:+DisableAttachMechanism";
         String debug = "true";
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " excludePrefix=" + excludePrefix

+ 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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true

+ 88 - 0
class-winter-core/src/test/java/winter/com/ideaaedi/classwinter/EncryptArgParam_Test.java

@@ -0,0 +1,88 @@
+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.Constant;
+import winter.com.ideaaedi.classwinter.util.PathUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 测试加密参数
+ * <p>
+ *     注:在includePrefix后面允许设置加密参数(方式同在url后加参数一样,只是不需要urlEncoding)
+ *
+ * @author {@link JustryDeng}
+ * @since 2021/6/12 14:34:45
+ */
+public class EncryptArgParam_Test {
+    
+    public static void main(String[] args) {
+//        // 测试1: 无参数(默认参数)测试
+//        Map<String, String> argMap = new HashMap<>();
+//        have(argMap);
+    
+//        // 测试2: 有参数测试
+//        Map<String, String> argMap = new HashMap<>();
+//        argMap.put(Constant.CLEAN_CLASS_ANNOTATION_PARAM_NAME, "true");  // 默认为false
+//        argMap.put(Constant.CLEAN_METHOD_ANNOTATION_PARAM_NAME, "true");  // 默认为false
+//        argMap.put(Constant.CLEAN_FIELD_ANNOTATION_PARAM_NAME, "true"); // 默认为false
+//        have(argMap);
+    
+//        // 测试3: 有参数测试
+//        Map<String, String> argMap = new HashMap<>();
+//        argMap.put(Constant.CLEAN_CLASS_ANNOTATION_PARAM_NAME, "false");  // 默认为false
+//        argMap.put(Constant.CLEAN_METHOD_ANNOTATION_PARAM_NAME, "true");  // 默认为false
+//        argMap.put(Constant.CLEAN_FIELD_ANNOTATION_PARAM_NAME, "false"); // 默认为false
+//        have(argMap);
+    
+        // 测试4: 有参数测试
+        Map<String, String> argMap = new HashMap<>();
+        argMap.put(Constant.CLEAN_CLASS_ANNOTATION_PARAM_NAME, "true");  // 默认为false
+        argMap.put(Constant.CLEAN_METHOD_ANNOTATION_PARAM_NAME, "false");  // 默认为false
+        argMap.put(Constant.CLEAN_FIELD_ANNOTATION_PARAM_NAME, "true"); // 默认为false
+        have(argMap);
+    }
+    
+    /**
+     * jar测试 - 失败测试
+     */
+    public static void have(Map<String, String> argMap) {
+        // 杀下进程(以保证端口没有被占用)
+        BashUtil.killProcessByPorts("8080");
+        
+        String projectRootDir = PathUtil.getProjectRootDir(AlreadyProtectedLibs_Test.class);
+        String originJarOrWar = projectRootDir + "boot-jar.jar";
+        String includePrefix = "com";
+        
+        // 在includePrefix后面组装参数(和在url后面增加参数一样)
+        if (argMap != null && argMap.size() > 0) {
+            StringBuilder sb = new StringBuilder(includePrefix);
+            sb.append("?");
+            argMap.forEach((key, value) -> {
+                sb.append(key).append("=").append(value).append("&");
+            });
+            sb.replace(sb.length() - 1, sb.length(), "");
+            includePrefix= sb.toString();
+        }
+        
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
+                + " originJarOrWar=" + originJarOrWar
+                + " includePrefix=" + includePrefix
+                + " debug=true"
+                ;
+        // 加密
+        BashUtil.runBashAndPrint(startBat);
+    
+    
+        String encryptedJar = originJarOrWar.replace(".jar", "-encrypted.jar");
+    
+        // 解密
+        String javaagentArgs = "";
+        // String javaagentArgs = "=debug=true";
+        // String javaagentArgs = "=debug=true,password=xxx";
+        BashUtil.runBashAndPrint(String.format("java -javaagent:%s%s=debug=true -jar %s", encryptedJar, javaagentArgs, encryptedJar));
+    }
+    
+}

+ 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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true

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

@@ -33,7 +33,7 @@ public class JvmArgCheck_Test {
     
         String jvmArg = "-XX:+DisableAttachMechanism,-Xms1024M";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " jvmArgCheck=" + jvmArg
@@ -65,7 +65,7 @@ public class JvmArgCheck_Test {
     
         String jvmArg = "-XX:+DisableAttachMechanism,-Xms1024M";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " jvmArgCheck=" + jvmArg
@@ -97,7 +97,7 @@ public class JvmArgCheck_Test {
     
         String jvmArg = "-XX:+DisableAttachMechanism";
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " jvmArgCheck=" + jvmArg

+ 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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " debug=" + true

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

@@ -34,7 +34,7 @@ public class Password_Test {
         String includePrefix = "com";
         String password = "pwd123";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " password=" + password
@@ -65,7 +65,7 @@ public class Password_Test {
         String includePrefix = "com";
         String password = "pwd123";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " password=" + password
@@ -97,7 +97,7 @@ public class Password_Test {
         String includePrefix = "com";
         String password = "pwd123";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.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.7.9.jar originJarOrWar=/boot-jar.jar includePrefix=com tips="[潘嘎之交] 潘: 你别怪潘叔说话比较直白, 网络上的东西都是虚拟的, 你把握不住, 孩子! 因为这里的水很深..."
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        // java -jar class-winter-core-2.8.0.jar originJarOrWar=/boot-jar.jar includePrefix=com tips="[潘嘎之交] 潘: 你别怪潘叔说话比较直白, 网络上的东西都是虚拟的, 你把握不住, 孩子! 因为这里的水很深..."
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " tips=" + (BashUtil.IS_WINDOWS ? "\"".concat(tips).concat("\"") : "'".concat(tips).concat("'"));

+ 5 - 5
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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 ;
@@ -62,7 +62,7 @@ public class War_Test {
         String includePrefix = "com";
         String password = "123456";
     
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " password=" + password
@@ -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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " alreadyProtectedLibs=" + alreadyProtectedLibs
@@ -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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " alreadyProtectedLibs=" + alreadyProtectedLibs
@@ -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.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " excludePrefix=" + excludePrefix

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

@@ -31,7 +31,7 @@ public class Xml_Test {
         String toCleanXmlChildElementName = "select";
         
         
-        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.7.9.jar"
+        String startBat = "java -jar " + projectRootDir + "class-winter-core-2.8.0.jar"
                 + " originJarOrWar=" + originJarOrWar
                 + " includePrefix=" + includePrefix
                 + " includeXmlPrefix=" + includeXmlPrefix

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


BIN
class-winter-core/src/test/resources/class-winter-core-enhance-2.8.0.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.7.9</version>
+        <version>2.8.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
pom.xml

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