|
@@ -1,12 +1,16 @@
|
|
|
package winter.com.ideaaedi.classwinter.executor;
|
|
package winter.com.ideaaedi.classwinter.executor;
|
|
|
|
|
|
|
|
|
|
+import com.sun.org.apache.xerces.internal.impl.Constants;
|
|
|
import javassist.CannotCompileException;
|
|
import javassist.CannotCompileException;
|
|
|
import javassist.ClassPool;
|
|
import javassist.ClassPool;
|
|
|
import javassist.NotFoundException;
|
|
import javassist.NotFoundException;
|
|
|
import org.dom4j.Document;
|
|
import org.dom4j.Document;
|
|
|
|
|
+import org.dom4j.DocumentException;
|
|
|
import org.dom4j.Element;
|
|
import org.dom4j.Element;
|
|
|
import org.dom4j.io.SAXReader;
|
|
import org.dom4j.io.SAXReader;
|
|
|
import org.dom4j.io.XMLWriter;
|
|
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.Reverses;
|
|
|
import winter.com.ideaaedi.classwinter.author.JustryDeng;
|
|
import winter.com.ideaaedi.classwinter.author.JustryDeng;
|
|
|
import winter.com.ideaaedi.classwinter.exception.ClassWinterException;
|
|
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.SimpleFileOrderSupport;
|
|
|
import winter.com.ideaaedi.classwinter.util.StrUtil;
|
|
import winter.com.ideaaedi.classwinter.util.StrUtil;
|
|
|
|
|
|
|
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
import java.io.File;
|
|
import java.io.File;
|
|
|
import java.io.FileOutputStream;
|
|
import java.io.FileOutputStream;
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.io.StringReader;
|
|
|
|
|
+import java.lang.reflect.UndeclaredThrowableException;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.security.SecureRandom;
|
|
import java.security.SecureRandom;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
@@ -100,6 +108,23 @@ public class EncryptExecutor {
|
|
|
*/
|
|
*/
|
|
|
private final String password;
|
|
private final String password;
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 要加密的xml文件的zipEntry名前缀(如:)
|
|
|
|
|
+ */
|
|
|
|
|
+ private final Set<String> includeXmlPrefixSet;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 要排除加密的xml文件的zipEntry名前缀
|
|
|
|
|
+ */
|
|
|
|
|
+ private final Set<String> excludeXmlPrefixSet;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * xml中要清除的(根节点的)子节点名称
|
|
|
|
|
+ */
|
|
|
|
|
+ private final Set<String> toClearChildElementNameSet;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 要加密的类的全类名前缀(也可以精确匹配)
|
|
* 要加密的类的全类名前缀(也可以精确匹配)
|
|
|
*/
|
|
*/
|
|
@@ -173,8 +198,10 @@ public class EncryptExecutor {
|
|
|
private final Map<String, String> libJarAndTmpDirMap = new HashMap<>(8);
|
|
private final Map<String, String> libJarAndTmpDirMap = new HashMap<>(8);
|
|
|
|
|
|
|
|
private EncryptExecutor(String originJarOrWar, boolean originIsJar, String finalName, String targetRootDir,
|
|
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> toClearChildElementNameSet,
|
|
|
|
|
+ Set<String> includePrefixSet, Set<String> excludePrefixSet, Set<String> includeLibSet,
|
|
|
|
|
+ Set<Pair<String, String>> alreadyProtectedLibSet, String supportFile) {
|
|
|
this.originJarOrWar = originJarOrWar;
|
|
this.originJarOrWar = originJarOrWar;
|
|
|
this.originIsJar = originIsJar;
|
|
this.originIsJar = originIsJar;
|
|
|
this.finalName = finalName;
|
|
this.finalName = finalName;
|
|
@@ -182,6 +209,9 @@ public class EncryptExecutor {
|
|
|
this.targetLibDir = targetLibDir;
|
|
this.targetLibDir = targetLibDir;
|
|
|
this.targetClassesDir = targetClassesDir;
|
|
this.targetClassesDir = targetClassesDir;
|
|
|
this.password = password;
|
|
this.password = password;
|
|
|
|
|
+ this.includeXmlPrefixSet = includeXmlPrefixSet;
|
|
|
|
|
+ this.excludeXmlPrefixSet = excludeXmlPrefixSet;
|
|
|
|
|
+ this.toClearChildElementNameSet = toClearChildElementNameSet;
|
|
|
this.includePrefixSet = includePrefixSet;
|
|
this.includePrefixSet = includePrefixSet;
|
|
|
this.excludePrefixSet = excludePrefixSet;
|
|
this.excludePrefixSet = excludePrefixSet;
|
|
|
this.includeLibSet = includeLibSet;
|
|
this.includeLibSet = includeLibSet;
|
|
@@ -200,60 +230,126 @@ public class EncryptExecutor {
|
|
|
|
|
|
|
|
// step0. 解压jar到jarRootDir下
|
|
// step0. 解压jar到jarRootDir下
|
|
|
List<String> filePathList = JarUtil.unJarWar(originJarOrWar, targetRootDir);
|
|
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)进行解压,(解压到对应的临时目录,以便后面进行加密处理)
|
|
// step1. jar中的某些lib可能也需要加密,这里先(将那些需要加密的lib)进行解压,(解压到对应的临时目录,以便后面进行加密处理)
|
|
|
unLibJar();
|
|
unLibJar();
|
|
|
- showProcess("step01", "step11", "un-lib-jar");
|
|
|
|
|
|
|
+ showProcess("step02", lastStep, "un-lib-jar");
|
|
|
|
|
|
|
|
// step2. 找到需要加密混淆的class文件
|
|
// step2. 找到需要加密混淆的class文件
|
|
|
- File targetRootDirFile = new File(targetRootDir);
|
|
|
|
|
List<File> allFiles = IOUtil.listSubFile(targetRootDirFile, 1);
|
|
List<File> allFiles = IOUtil.listSubFile(targetRootDirFile, 1);
|
|
|
List<File> allNeedEncryptedClassFileList = filterClasses(allFiles);
|
|
List<File> allNeedEncryptedClassFileList = filterClasses(allFiles);
|
|
|
- showProcess("step02", "step11", "filter-classes");
|
|
|
|
|
|
|
+ showProcess("step03", lastStep, "filter-classes");
|
|
|
|
|
|
|
|
// step3.1. 根据原.class文件,生成加密后的.class文件,并存至Constant.ENCRYPTED_CLASSES_SAVE_DIR
|
|
// step3.1. 根据原.class文件,生成加密后的.class文件,并存至Constant.ENCRYPTED_CLASSES_SAVE_DIR
|
|
|
List<String> allAlreadyEncryptedClassFileList = encryptClasses(allNeedEncryptedClassFileList, new File(targetRootDir,
|
|
List<String> allAlreadyEncryptedClassFileList = encryptClasses(allNeedEncryptedClassFileList, new File(targetRootDir,
|
|
|
Constant.DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR));
|
|
Constant.DEFAULT_ENCRYPTED_CLASSES_SAVE_DIR));
|
|
|
// step3.2. 生成记录已加密class的全类名的清单文件
|
|
// step3.2. 生成记录已加密class的全类名的清单文件
|
|
|
generateChecklistFile(allAlreadyEncryptedClassFileList);
|
|
generateChecklistFile(allAlreadyEncryptedClassFileList);
|
|
|
- showProcess("step03", "step11", "un-jar-war");
|
|
|
|
|
|
|
+ showProcess("step04", lastStep, "un-jar-war");
|
|
|
|
|
|
|
|
// step4. 记录印章(在解密时,可通过 checklist + 印章 判断一个class是否是被class-winter加密过)
|
|
// step4. 记录印章(在解密时,可通过 checklist + 印章 判断一个class是否是被class-winter加密过)
|
|
|
generateSealFile();
|
|
generateSealFile();
|
|
|
- showProcess("step04", "step11", "encrypt-classes");
|
|
|
|
|
|
|
+ showProcess("step05", lastStep, "encrypt-classes");
|
|
|
|
|
|
|
|
// step5. 混淆原.class文件(即:清空方法体)
|
|
// step5. 混淆原.class文件(即:清空方法体)
|
|
|
clearClassMethod(allNeedEncryptedClassFileList);
|
|
clearClassMethod(allNeedEncryptedClassFileList);
|
|
|
- showProcess("step05", "step11", "clear-class-method");
|
|
|
|
|
|
|
+ showProcess("step06", lastStep, "clear-class-method");
|
|
|
|
|
|
|
|
// step6. 清除META-INF/maven下pom.xml中关于class-winter-plugin的相关信息
|
|
// step6. 清除META-INF/maven下pom.xml中关于class-winter-plugin的相关信息
|
|
|
clearWinterPluginInfo();
|
|
clearWinterPluginInfo();
|
|
|
- showProcess("step06", "step11", "clear-winter-plugin-info");
|
|
|
|
|
|
|
+ showProcess("step07", lastStep, "clear-winter-plugin-info");
|
|
|
|
|
|
|
|
// step7. 添加class-winter的用于javaagent解密的代码
|
|
// step7. 添加class-winter的用于javaagent解密的代码
|
|
|
addClassWinterAgent();
|
|
addClassWinterAgent();
|
|
|
- showProcess("step07", "step11", "add-class-winter-agent");
|
|
|
|
|
|
|
+ showProcess("step08", lastStep, "add-class-winter-agent");
|
|
|
|
|
|
|
|
// step8. 汇总那些在本次加密前本身就已经是被class-winter混淆的lib的相关信息到当前项目中
|
|
// step8. 汇总那些在本次加密前本身就已经是被class-winter混淆的lib的相关信息到当前项目中
|
|
|
collectAlreadyProtectedLibInfo();
|
|
collectAlreadyProtectedLibInfo();
|
|
|
- showProcess("step08", "step11", "collect-already-protected-lib-info");
|
|
|
|
|
|
|
+ showProcess("step09", lastStep, "collect-already-protected-lib-info");
|
|
|
|
|
|
|
|
// step9. 与step1项对应,将临时目录还原为原来的lib(此时得到的lib是加密后的)
|
|
// step9. 与step1项对应,将临时目录还原为原来的lib(此时得到的lib是加密后的)
|
|
|
doLibJar();
|
|
doLibJar();
|
|
|
- showProcess("step09", "step11", "do-lib-jar");
|
|
|
|
|
|
|
+ showProcess("step10", lastStep, "do-lib-jar");
|
|
|
|
|
|
|
|
// step10. 压缩targetRootDir目录为绝对文件路径名为generatedJarWar的jar(or war)包
|
|
// step10. 压缩targetRootDir目录为绝对文件路径名为generatedJarWar的jar(or war)包
|
|
|
String generatedJarWar = generateJarWarPath(this.originIsJar);
|
|
String generatedJarWar = generateJarWarPath(this.originIsJar);
|
|
|
JarUtil.doJarWar(targetRootDir, generatedJarWar, new SimpleFileOrderSupport(filePathList));
|
|
JarUtil.doJarWar(targetRootDir, generatedJarWar, new SimpleFileOrderSupport(filePathList));
|
|
|
- showProcess("step10", "step11", "generate-jar-war-path");
|
|
|
|
|
|
|
+ showProcess("step11", lastStep, "generate-jar-war-path");
|
|
|
|
|
|
|
|
// step11. 删除临时目录
|
|
// step11. 删除临时目录
|
|
|
IOUtil.delete(targetRootDirFile);
|
|
IOUtil.delete(targetRootDirFile);
|
|
|
- showProcess("step11", "step11", "delete-tmp-dir");
|
|
|
|
|
|
|
+ showProcess(lastStep, lastStep, "delete-tmp-dir");
|
|
|
return generatedJarWar;
|
|
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), Constant.XML_PADDING_COMMENT, toClearChildElementNameSet);
|
|
|
|
|
+ IOUtil.writeContentToFile(cleanedXml, new File(targetRootDir, zipEntryName));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* showProcess
|
|
* showProcess
|
|
|
*
|
|
*
|
|
@@ -397,7 +493,7 @@ public class EncryptExecutor {
|
|
|
return;
|
|
return;
|
|
|
} else {
|
|
} else {
|
|
|
String libChecklist = new String(checklistByte, StandardCharsets.UTF_8);
|
|
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
|
|
// 所有lib的checklist汇总至checklistOfAllLibs
|
|
|
allChecklistInfoMap.put(dirRelativePathForLib, libChecklist);
|
|
allChecklistInfoMap.put(dirRelativePathForLib, libChecklist);
|
|
@@ -733,6 +829,68 @@ public class EncryptExecutor {
|
|
|
}).collect(Collectors.toList());
|
|
}).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
|
|
@Override
|
|
|
public String toString() {
|
|
public String toString() {
|
|
@@ -747,8 +905,11 @@ public class EncryptExecutor {
|
|
|
", password='" + (StrUtil.isBlank(password) ? null : "******") + '\'' +
|
|
", password='" + (StrUtil.isBlank(password) ? null : "******") + '\'' +
|
|
|
", supportFile='" + supportFile + '\'' +
|
|
", supportFile='" + supportFile + '\'' +
|
|
|
", includePrefixSet=" + includePrefixSet +
|
|
", includePrefixSet=" + includePrefixSet +
|
|
|
- ", includeLibSet=" + includeLibSet +
|
|
|
|
|
", excludePrefixSet=" + excludePrefixSet +
|
|
", excludePrefixSet=" + excludePrefixSet +
|
|
|
|
|
+ ", includeXmlPrefixSet=" + includeXmlPrefixSet +
|
|
|
|
|
+ ", excludeXmlPrefixSet=" + excludeXmlPrefixSet +
|
|
|
|
|
+ ", toClearChildElementNameSet=" + toClearChildElementNameSet +
|
|
|
|
|
+ ", includeLibSet=" + includeLibSet +
|
|
|
", alreadyProtectedLibSet=" + alreadyProtectedLibSet +
|
|
", alreadyProtectedLibSet=" + alreadyProtectedLibSet +
|
|
|
'}';
|
|
'}';
|
|
|
}
|
|
}
|
|
@@ -772,6 +933,12 @@ public class EncryptExecutor {
|
|
|
|
|
|
|
|
private String excludePrefix;
|
|
private String excludePrefix;
|
|
|
|
|
|
|
|
|
|
+ private String includeXmlPrefix;
|
|
|
|
|
+
|
|
|
|
|
+ private String excludeXmlPrefix;
|
|
|
|
|
+
|
|
|
|
|
+ private String toClearChildElementName;
|
|
|
|
|
+
|
|
|
private String includeLibs;
|
|
private String includeLibs;
|
|
|
|
|
|
|
|
private String alreadyProtectedLibs;
|
|
private String alreadyProtectedLibs;
|
|
@@ -806,6 +973,21 @@ public class EncryptExecutor {
|
|
|
this.excludePrefix = excludePrefix;
|
|
this.excludePrefix = excludePrefix;
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ public Builder includeXmlPrefix(String includeXmlPrefix) {
|
|
|
|
|
+ this.includeXmlPrefix = includeXmlPrefix;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public Builder excludeXmlPrefix(String excludeXmlPrefix) {
|
|
|
|
|
+ this.excludeXmlPrefix = excludeXmlPrefix;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public Builder toClearChildElementName(String toClearChildElementName) {
|
|
|
|
|
+ this.toClearChildElementName = toClearChildElementName;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
public Builder includeLibs(String includeLibs) {
|
|
public Builder includeLibs(String includeLibs) {
|
|
|
this.includeLibs = includeLibs;
|
|
this.includeLibs = includeLibs;
|
|
@@ -838,7 +1020,8 @@ public class EncryptExecutor {
|
|
|
Logger.ENABLE_DEBUG.set(this.debug != null && this.debug);
|
|
Logger.ENABLE_DEBUG.set(this.debug != null && this.debug);
|
|
|
// 2. 错误启动jar时,System.out输出的提示信息
|
|
// 2. 错误启动jar时,System.out输出的提示信息
|
|
|
if (!StrUtil.isEmpty(tips)) {
|
|
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]");
|
|
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);
|
|
|
|
|
+ toClearChildElementName = StrUtil.isBlank(toClearChildElementName) ? Constant.DEFAULT_XML_NODE_NAMES : toClearChildElementName;
|
|
|
|
|
+ Set<String> toClearChildElementNameSet = StrUtil.strToSet(toClearChildElementName);
|
|
|
return new EncryptExecutor(this.originJarOrWar, originIsJar, this.finalName, targetRootDir, targetLibDir, targetClassesDir,
|
|
return new EncryptExecutor(this.originJarOrWar, originIsJar, this.finalName, targetRootDir, targetLibDir, targetClassesDir,
|
|
|
- password, includePrefixSet, excludePrefixSet, includeLibSet, alreadyProtectedLibSet, this.supportFile);
|
|
|
|
|
|
|
+ password, includeXmlPrefixSet, excludeXmlPrefixSet, toClearChildElementNameSet, includePrefixSet, excludePrefixSet, includeLibSet, alreadyProtectedLibSet, this.supportFile);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|