背景

我们在公司内网写代码、打包、部署都是在内网专有机器和专有网络上访问,所以基本不会考虑代码加密等问题。

如果我们的代码要部署到公有云等外部机器上,或者与其他公司共建等共享机房内部署,那么就要考虑我们部署的代码要进行加密后上传到公有云机器上部署,从而避免别的公司会下载我们的部署包反编译查看我们的代码。

如果我们的代码不进行加密等处理,那我们的业务极有可能

1:被破解,内部数据隐私等泄露的风险。

2:也很有可能被恶意通过字节码植入破坏性代码。

3:被破解者绕过关键逻辑造成经济损失。

正是要解决此类问题,我们不得不对代码进行加固处理。

预研

Jar包的加密在正常场景中运用较少,基本不会考虑此类加密处理,一般是在加密数据等领域居多,和Android的SDK包等发布加密居多,一般简单的使用代码混淆proguard插件就能满足一些需求,但是该工具使用范围极其有限,简单模式的java模块还是可以满足的。

市面的收费产品都是自研的整体加密方式,配合agent启动解密(该方向是java方向主流的Spring包应用),在实现上确实有方便和独到之处。

对比

下面的表格是我居于两种产品的对比列表

代码混淆 Class文件加密
原理 将项目代码中包名、类名、方法名、参数名皆可混淆的工具打包插件,打包后的代码很难看 代码打包后将Jar中的class文件解压再加密后重新打包到Jar中,包名、类名、方法名等没有变化,仅对class内容加密
启动方式区别 java -jar即可启动,没有改变 必须利用javaagent在启动时将class内容解密还原再给ClassLoad加载,所以需依赖一个-javaagent
源码反编译区别 代码利用JD-GUI即可查看源码,或IDEA中也可查看jar包源码,但混淆后的源码极其难看难理解其含义 无法反编译源码,如需查看源码需知晓密码,并且解密方式繁琐
代码输出方式 混淆后的代码输出为一个单独包含class的jar包,并非在fat jar里面并成整体。这就带来了一个问题,就是先打混淆jar包,后通过再maven中引用jar包,并非能一步到位 Class加密后的代码和Fat Jar是一个整体,另外的一个agent jar包可以提前预置在发布脚本和机器中。即时jar包不属于加密包,启动也不会有影响,利用配置参数判断是否需要自动加入-javaagent参数
隐患 1、包名、类名的混淆会让Spring对特性的包扫描失效(如aop切面); 2、方法名混淆会让jpa等第三方代理方法无法识别方法名; 3、类似ibatis的接口代理话可能出现以一些无法识别的问题。 4、对有些第三包用了高版本JDK编译不支持。 1、启动时对代码解密的agent需要稳定的解密测试; 2、对本身依赖多个agent的应用要优先把解密的本agent设置最高优先级。
测试 混淆后的代码出现了一些切面代码无法识别的问题和代码的测试难度增加,需开发者自己Cover住 源码测试和正常情况一样,只需打包后部署一次完整测试即可。即:开发者不关心Jar加固的问题,只关心自己实现的功能代码即可
是否可标准化 混淆代码是不能标准化的,只能开发者自行做代码混淆的pom定义,不能在Jenkins等工具进行统一化管理 打包可以使用利用maven package自定义插件进行打包,定义该插件打包即可,只需要在启动命令加配置判断是否需要标识该jar包是加密后的jar,就用解密jar方式添加-javaagent
使用难度 能正常打包没有异常就说明可用,很多异常不好看出问题,排除难度有些大 对agent加解密的代码需要很深的ClassLoad理解,个人担心的隐患在于agent中的解密和ClassLoad加载的问题不好判断;但对开发者是友好的,无需额外担心自身代码的问题。
工具开发难度 1、无需开发。 2、在pom引入的maven plugin支持,应用开发者按帮助文档引用插件即可。 1、需开发。 2、需开发加解密的工具、agent包,并对解密agent的性能影响评估需更深入的测试
业界对比 因对Spring支持不友好,应用与SDK的打包居多,更多是工具包、加密信息的打包 很多付费的Jar加固方式都是用此方式,彻底屏蔽了反编译的路子,支持源码包加密,也可以对第三方包加密
最终制品支持 该方式是SDK单独加密,和最终输出的包是Jar或War无关,等于皆可支持 War和Jar都能运行-javaagent,所以皆可支持
加密方式 自有的一套对名称混淆的逻辑。 可自定义加密方式(AES/RES…),可利用机器码进行加密,这样即可防止Jar被窃取后反编译破解的难度,仅限指定的机器码进行解码,但在容器部署的场景这个难支持。

混淆示例

image-20220412170023522

混淆后的代码是被一种特殊规则命名修改后的结果,最终还是选择自研的class文件加密方式,配合javaagent来运行。

我们正常的jar包里面的内容是什么样的呢?我们要怎么对现有的jar包进行加固呢?所以怎么下手是一个必须要面对的难题。我们以Spring-Boot的jar包为例,也是主流jar打包方式。

WX20220406-175405@2x

正常Spring-Boot jar包解压后有3个文件夹

BOOT-INT:存放该应用的第一方代码(即非maven引入的jar,你在这个项目可直接编辑的代码)

META-INT:主要是配置文件,MAINFEST.MF是文件启动描述文件,Application.java全路径启动描述

org:Spring-Boot的启动依赖类,Spring-Boot自己的类加载用的依赖类

WX20220406-175557@2x

确认方向

从文件路径图中看,我们其实要处理的就一个文件夹「BOOT-INF」,因为这里存放的是原始第一手class文件,和依赖的二方包、三方包jar文件,也就是要防止别人看的文件。

那BOOT-INF文件夹分四类,一类是java文件,一类是静态前端文件,一类是配置文件都在classes文件夹内,lib是maven引入的jar包文件。

我们观察一下classes的class必然要加密,静态前端文件基本都是vue用node编译后的文件,这种文件已经极其难看了就可以不用考虑加密处理了。配置文件呢我们一般是用第三方配置系统的,所以application.properties内存放的东西很少,也不会存放敏感信息的(如有敏感信息建议放第三方配置平台),所以也不用对这类文件做出来。lib下的第二方包我们得做加密,为啥呢?因为我们有很多api包、sdk包是自己写的模块代码,打成了jar包后给本应用模块依赖进来的,这些可能都是敏感文件肯定也不能给暴露了。其他的jar包就不用去加密了,因为这些第三方包基本都是开源的,或者不是业务相关的工具jar没必要加密。

综合下来就classes的class文件和lib包的我们自己敏感二方包要加密。

确定了要处理的文件后我们要确认一下行不行的通,我加密这些代码后会不会启动都启动不了了,我们启动jar包这些class怎么还原回来?

我搜索了一下氪金产品,jar包加固都是提供一个加密软硬把包丢进去生成一个新的加密包,在提供一个javaagent包配合启动时解密,因为启动jar包时,javaagent是第一道关卡,从这个关卡中把class还原回来给class loader重定义后主程序就能用解密还原回来的class。

Instrumentation.addTransformer()
//方法允许我们在类加载之前,重新定义Class,先看一下方法的定义:
void addTransformer(ClassFileTransformer transformer);
//ClassFileTransformer是一个接口,只有一个transform方法,它在主程序的main方法执行前,装载的每个类都要经过transform执行一次,可以将它称为转换器。

Javaagent这点特性很重要,它恰好解决了我们要类加载之前把class还原回来。用javaagent的话需要在启动时加启动参数配置,可以在启动sh包内加-D等参数判断,如果不是做过jar包加密的应用就不用加javaagent的参数,写在部署脚本里面这样也挺通用的。

不过生成jar包不能用一个文件人工执行,我们要自己做这个实现,那我们得用到加解密的工具类如RSA、AES这类的非对称加解密文件,打包的工具我们要配合Jenkins那我们可以用脚本去运行Main方法进行jar加密后生成一个新的,或者在打包时用maven插件直接把打包后的包进行jar加密输出成另外一个包,但是要配合插件的参数判断是否要启用jar加密还是不用加密,这里可能会麻烦一点,那直接就用Main方法加密吧,写在Jenkins自定义脚本去执行。

如果我们需要指定只能在某台机器上解密的话也得要支持,比如用mac地址,只能在这个mac地址的机器才能执行启动加解密,那也挺保险的,但是这个配置可能会导致一些问题不太灵活。比如扩容机器、机器坏了换机器那你得重新打包,紧急扩容的时候就很难受了。除非这些配置你在javaagent启动的时候先去远程调用配置中心的配置来做,但是这个配置存放在哪可能会有一些问题,比如你这个公有云是不能访问其他云网络的,你只能在你的公有云里面部署一下配置管理系统来做这个接口,这样确实有些启动依赖在里面的。

是否要做这个第三方配置或许取决于小组讨论,主要就是还得考虑一下如果获取配置接口挂了的情况就无法启动了。

我们规划一下这个jar包加固的脚手架

  • jarx-core: Jarx的核心模块,对jar包解压、包下class加密功能,重新压缩成jar包;
  • jarx-crypto: Jarx的加密模块,用什么方式加密、解密;
  • jarx-agent: Jarx的agent启动依赖解密模块;
  • jarx-fatjar: Jarx的打包工具模块,从控制台中获取一系列参数调用core包加密jar(Jenkins自定义脚本就是要调用这个jar,需要提前打包好这个工具放到一个挂载盘中给Jenkins下载执行);
  • jarx-maven-plugin: Jarx的maven打包插件;

fatjar和maven-plugin都是做加密打包用,就看用那种方便吧。

我们要定义一些Main方法打包的参数,因为我想做的是classes包可以像aop已经切面去指定要加密的class,指定lib下哪些jar要加密,这些加的aop包扫描也要说明,哪些类可以设置不加密。

参数说明

参数名 说明 是否必填
-filePath 加密的jar完整路径 必填
-packages 加密的包名,该包下所有class都会加密,多个用”,”分割 必填
-publicPassword 公钥 必填
-password 原始密码,不填则随机生成  
-excludeClasses 排除的全类名,多个用”,”分割  
-includeJars jar包lib下要加密jar文件名,多个用”,”分割  
-macCodes Mac地址,在绑定的机器生成,加密后只可在此机器上运行  

执行命令,配置用等于号进行配置,用空格隔开下一个配置

java -jar jarx-fatjar.jar -filePath=xxx-app.jar -includeJars=a.jar,b.jar -packages=com.xxx.应用a.modules,com.xxx.应用b.modules -excludeClasses=com.xxx.Application -publicPassword=xxx -password=123456 -macCodes=90-9C-4A-CB-BC-FF

结果: 生成 xxx-app-encrypted.jar,这个就是加密后的jar文件;加密后的文件不可直接执行,需要配合javaagent启动。

整体架构图如下

流程

我们知道我们要实现的脉络后并生成了脚手架模块代码,接下来的事情就一路把功能代码填补上。

整体流程图

流程

Jar包处理

-filePath

按我们定义的参数第一个是待加密的jar包路径,先判断文件是否存在,是否是jar类型的扩展名。

-publicPassword

接下来是公钥,为什么是公钥,这里的加密设计是用RSA对一个原始密码加密,存放到一个文件内,如果有指定Mac地址,也用RSA加密放到另外一个文件内,这里多出两个文件,那就一起定义加密后的文件存放到哪里好了,从jar的目录结果来看,BOOT-INF是原始文件夹,还有一个是META-INF文件夹,我选择放META-INF文件夹里面用点号开头命名隐藏文件夹如:.xxxClasses。

类uninx操作系统,以“.”开头的文件或文件夹,表示隐藏文件,ls,命令默认不显示

image-20220407142756538

这个浅颜色表示隐藏文件,如果没有设置显示隐藏文件很难看出来加密后的文件存于此。而加密后的密码和Mac地址群都在里面。

image-20220407144545022

-macCodes

Code和Pass文件都是RSA加密后的字符串,多个Mac地址就用逗号隔开即可。这里再次说明一下Mac地址校验问题,为什么要用逗号隔开这样设计而不是一个Mac地址打一个包。考虑到打制品包只能用一个最终制品,避免多制品不一致问题,打多个Mac地址包确实更安全,一个包传一个机器去,但是Jenkins打完后传到哪里这一步骤你在哪做都非常麻烦,要映射Mac和ip所在的机器连接关系,明显不现实且损失非常大,而且每打一个包要标明这个包对应的是那个ip用的,所以把Mac地址群放到一个Code文件里面是比较省事的方法。

所以这里公钥参数的用法表达完了,也在代码里面约定了加密后的文件存储到META-INF/.xxxClasses文件夹下。

-includeJars

是否有二方包的代码需要加密,有就把二方包的jar解压,解压的临时目录就在当前的lib下,我这里的逻辑是大jar包建立一个临时目录_temp_来存放,二方包的目录就建立和二方包名称在加_temp_来存储,二方包哪些文件夹下的文件要加密就配合-packages参数使用,目录解构如图所示:

WX20220406-183741

-packages

待加密的文件代码我们按aop的切面方式进行配置,这种方式非常有利于我们对BOOT-INF下的源文件进行IO操作,因为包名就是文件夹的名称。我们只要解压得到文件夹,就可以把该文件夹下的所有文件遍历去加密即可,最后在xxxClasses文件夹内用java包名方式存储加密后的文件即可。

image-20220407144401281

class文件的加密是用原始密码进行AES方式加密的,所以密码有两道关,如果要解密class文件要知道原始密码和加密方式,得到原始密码要知道密码文件Pass所在的路径,并用私钥解密后得到原始密码再AES解密class就能读出正常class内容。

-excludeClasses

是否有排除掉不需要加密的class类,这里可以按需指定。

重写原始class

对原始的class文件进行重写,把原本的方法体全部清空,只保留注释、注解、方法正确返回值。

为什么要做这一步呢?能不能把class全部删掉,或者写一些乱七八糟的内容在里面?这里我们要知道:

1:类加载双亲委派模型,全部交由父类加载器从上Bootstrap -> Ext -> App -> 自己的类加载。

2:我们有些API生成器是要从Jar包中直接解析得出的,或者运行启动时扫描BOOT-INF下的代码生成的。

如果我们把BOOT-INF代码删了,或者变成一段不可运行的文本会导致怎么样?

1:API文档无法支持,因为API是去加载代码和扫描注释生成的,这里可能代码都无法被加载就报错了,间接会影响到别人的一些文档工作。

2:类加载问题,我们做个实验,把Application.class删掉会影响什么,启动时立马报错。

WX20220406-174737

为啥呢?因为SpringBoot的启动类必须要加载这个Application,类加载就会到这个路径下找class转成byte[]给jvm。

WX20220406-175557@2x

3:如果我保留这个类不删,我把其他的删了总行吧?

也不行,为啥?因为Spring的Aop就是要根据包路径扫描的,你把代码全部删了,那Spring就认为这个路径下没有代码,那么Controller的注解那些就没有加载过,前端Ajax就请求不到资源。

4:那我能不能把代码都加密替换内容到里面?那我就不用把加密后的代码放META-INF了?省了一个空间。

也不行,还是类加载问题,因为第一手类加载不是你加载的,你加密后的文件第一次被类加载是顶层的Loader,你只能等它加载过一遍,传递到agent的时你赶紧把加密后的文件还原回来且ClassLoader重加载后再传递给应用去用。如果你的第一手文件Byte都是错的,那启动的时候就直接报错了。

所以,我们要想办法把原始class的方法体给清除掉,并且又能正常被加载,也就是还能作为一个正常的类来用。这也确保了如果别人拿到我们的包这个架子可以给你看,但是里面啥都没有,你看也看不出来是啥东西。看下图的效果:

image-20220407153717845

这么做的目的是保证Spring的包扫描路径下都有文件,都有完整的方法定义,就算你把方法体的内容简写掉,也必须保证‘定义句柄’被Spring所知道,后续进过自定义的ClassLoader还原方法内容即可正常使用。实现这个目标我们用了javassist工具,他提供了很方便的工具方法重写原本的类,又能保证我这个类还能用。

生成新jar包

最后就是把整个文件夹打包成新jar包

1:先打包lib下的jar,记得删掉_temp_结尾的临时目录。

2:把META-INF/maven文件夹给删掉,为啥?

因为这个文件夹存储了pom信息,如果用了maven插件方式来加密的话,你的秘钥或密码都会暴露有安全风险,我们的依赖信息和私服地址都在里面,然而这个文件删除也不会影响启动,删!

image-20220407154425224

3:再整体把_temp_打成fatjar就是最后的完整可交付的jar包了,最后删除_temp_目录完毕。

image-20220407154848009

整个jar包的处理逻辑流程和技术疑虑基本解决了。

Jar包的加密已经完毕了,现在开始做Jar包的解密开发,javaagent的开发工作量不多,只有是解密文件还原给应用使用。

agent加载问题注意

开发加密Jar包的时候我的规划是文件操作的代码规划到core包里面,可以依赖一些方便的工具如:Apache common-land、io。guava等,而crypto包只用jdk提供的类提供加密的方法,RSE和AES都能满足了,文件操作也可以用io包解决,因为agent解密只要依赖crypto的包,不会依赖core包。

agent最好不要依赖第三方包,除非你全部重写过第三方包和包名,否则容易出现传递依赖包冲突问题,比如你在agent包中用了guava的3.x版本,但是应用的包依赖的是guava的2.x版本,3.x版本删除过某个2.x版本的class,那么别人应用中用了这个类就会报错,因为传递依赖的时候agent是把2.x的代码打到他的class下面的,发现在agent有的情况下会优先用agent包内加载的代码,这点在写agent jar的时候是需要很谨慎的。

大家有兴趣可以了解一下skywalking源码,它的agent虽然有用第三方包但是打包的pom都排除掉了,仅利用应用里面懒加载的jar依赖传递,就是避免用了它的agent启动不会因为全类名一致导致代码冲突,并且skywalking中你用了什么功能它才会被激活,一旦被激活,你的jar包肯定是有该依赖,这也是它的厉害之处。

如果你的agent要用尽量都用<scope>provided</scope>来声明依赖,agent都去使用应用加载的第三方包。但注意,如果应用里面都没依赖过这个包,那启动的时候一用到这个包的类就异常了。

javaagent流程

我们加密的时候是把原始密码和Mac地址群分别放在Pass和Code文件里面,那么我们解密的时候是需要用到RSA私钥,那这个私钥存哪里也是个问题,目前暂定存放到环境变量中,如果觉得不稳妥,那就必须要在agent里面开发一个http去第三方配置中心取私钥了。

http接口是<可选>逻辑。

得到私钥后到META-INF下面找Pass和Code解密得到原始密码和判断是否有Mac地址授权判断。

public static void premain(String args, Instrumentation inst) {
  。。。。。。
  //解密后重新定义Class:
  AgentTransformer tran = new AgentTransformer(password);
  inst.addTransformer(tran);
}

Transformer类中根据className到META-INF/xxxClasses文件夹下寻找对应的类,如果存在就进行解密返回新的byte[],如果没有就返回原始byte[]。这里有个特殊的校验,即如何知道解密回来的文件是正常的class文件,如果你的文件被破坏了,可能就不是class文件,或者你的文件加密的时候是用txt格式内容,这里可能会转换错误的风险,我找了一些资料发现文件类型是有一个值可以区分的,就比如java文件有一个魔数,每个class文件前4个字节是CAFEBABE,Java语言的创始人设置的,爱喝咖啡的人有故事。

所以我只要判断是否为正常的class文件则解密正确,也保证的还原了文件,毕竟加密有问题就会导致整个包都不可用风险系数极高。

public class AgentTransformer implements ClassFileTransformer {
  @Override
  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                          ProtectionDomain domain, byte[] classBuffer) {
    if (className == null || domain == null || loader == null) {
      return classBuffer;
    }

    byte[] bytes = DECRYPT_UTILS.decode(projectPath, className, this.password);
    if (bytes == null) {
      return classBuffer;
    }
    if (isJavaMagicNumber(bytes)) {
      return bytes;
    } else {
      LOG.warn("解密到的文件格式非JAVA格式 :" + className);
    }
    return classBuffer;
  }

  private static final byte CA = -54;
  private static final byte FE = -2;
  private static final byte BA = -70;
  private static final byte BE = -66;

  /**
   * 魔数占四个字节
   */
  private boolean isJavaMagicNumber(byte[] bytes) {
    return bytes[0] == CA && bytes[1] == FE && bytes[2] == BA && bytes[3] == BE;
  }
}

禁用Attach

-XX:+DisableAttachMechanism

深度使用java的人基本都会用诊断功能如arthas工具,如何禁用其他工具attach到本进程呢?启动时加这个参数即可。

但是加这个参数意味这你放弃使用jcmd、jstack、jmap和jinfo等命令,因为该命令不允许工具与JVM连接。

启动效果

无论是懒加载或非懒加载,类被加载时都会先经过agent,agent作为应用的门卫角色,class被应用使用之前都会被优先校验或替换过。

image-20220407164650754

image-20220407164737747

当我们所有的功能实现都就绪的时候,就要考虑怎么触发这个打包问题了,最简单的我们写个Main方法来执行,有能力可以写一个maven plugin来做,参数在maven打包命令中可以传的。

我们先把Main方法这个实现了,这样用户其实不用修改他自己的源码pom任何东西都能用这个Jar包加固工具。

实现思路

在core包中我们已经把整个加密包的功能写好了,那就提供一个方法处理,参数包装一下给fatjat这个模块用。fatjar模块就只有一个类一个main方法,完事。但是遇到了一个问题,我把fatjar打成jar包后,怎么用java -jar xxx-fatjat.jar来传参和执行我们写的这个main函数呢?maven有提供一个插件的,就是配置一下指定的类,该类里面必须有main方法,java -jar的时候就会去命中这个类的main方法。

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.0.0</version>
  <configuration>
    <!-- 这个要配置一下,否则打包的时候额外生成一个没啥用的文件 -->
  	<createDependencyReducedPom>false</createDependencyReducedPom>
  </configuration>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
      <goal>shade</goal>
    </goals>
    <configuration>
      <transformers>
        <transformer
          implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
          <manifestEntries>
          	<Main-Class>com.xxx.jarx.fatjar.Main</Main-Class>
          </manifestEntries>
        </transformer>
      </transformers>
    </configuration>
    </execution>
  </executions>
</plugin>

main方法

最后就是如何校验java -jar 传的参数问题了,我们之前已经约定好的启动参数如下,只要安装我们约定的格式进行获取即可,

java -jar jarx-fatjar.jar -filePath=xxx-app.jar -includeJars=a.jar,b.jar -packages=com.xxx.应用a.modules,com.xxx.应用b.modules -excludeClasses=com.xxx.Application -publicPassword=xxx -password=123456 -macCodes=90-9C-4A-CB-BC-FF

public static void main(String[] args) {
  //args参数校验和获取...
	AppEncryptor encryptor = new AppEncryptor(dto);
  String result = encryptor.executeEncryptJar();
	//输出路径
}

整个jar包加密的流程已经诠释完成,跟谁集成怎么集成是后话,根据情况而定非公共工具要考虑的细节问题。

在此我想到一个细节,如果我们的agent、crypto包也应该要做代码混淆处理,因为放到公有云机房代码包也会有暴露的风险,我们自身也得把自身做一层混淆保护,这点利用混淆打包插件即可完成我们的工作。

Main方法打包方式用于自身测试比较方便,但在自动化打包方面并就显得繁琐,我们使用maven plugin来做,参数在maven打包命令中可以传,也可以在自身pom里面指定,两者均支持。

插件开发

打加密包的插件开发其实和Fatjar的逻辑差不多,就是调用core包提供的加密方法,而插件包中其实就是怎么获取参数的问题。

本章节略微说明一下maven plugin的开发中pom的配置如何获取,如需要了解maven plugin的原理请自行查找资料

我们定义了一个入口类,指明是package类型,maven打包到package之后,再执行这个入口逻辑,下面由@Parameter定义的属性名称其实就是<configuration>标签内的属性标签名称且一一对应,但我在该注解的defaultValue属性用了一个表达式,这是为了支持-D参数的设置,为什么说可以支持动态打包,就是用了这个表达式的作用。

优先级:pom.xml > -D

@Mojo(name = "xxxJarx", defaultPhase = LifecyclePhase.PACKAGE)
public class JarxPlugin extends AbstractMojo {
  @Parameter(defaultValue = "${mp.jarx.publicKey}")
  private String publicKey;
  @Parameter(defaultValue = "${mp.jarx.packages}")
  private String packages;
  @Parameter(defaultValue = "${mp.jarx.password}")
  private String password;
  @Parameter(defaultValue = "${mp.jarx.macCodes}")
  private String macCodes;
  @Parameter(defaultValue = "${mp.jarx.includeJars}")
  private String includeJars;
  @Parameter(defaultValue = "${mp.jarx.excludeClasses}")
  private String excludeClasses;
  
  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    if (StringUtils.isBlank(publicKey) ||
        StringUtils.isBlank(packages)) {
      //打印参数不全
      return;
    }
  }
}

如果是<configuration>标签内没有指定,那可以在灵犀流水线中去指定打包的参数(mp.jarx.开头,参数一样)。如果pom.xml加入了本maven plugin标签,但是必填字段不全,则不会启动打加密包逻辑。

mvn clean package -DskipTests -Dmp.jarx.publicKey=xxx -Dmp.jarx.-includeJars=应用B-1.0.0-RELEASE.jar -Dmp.jarx.packages=com.xxx.应用A.modules,com.xxx.应用B.modules
<plugin>
  <groupId>com.xxx.jarx</groupId>
  <artifactId>jarx-maven-plugin</artifactId>
  <version>1.0.0-RELEASE</version>
  <!-- begin configuration标签可以不指定,打包mvn脚本中可以动态指定 -->
  <configuration>
      <!-- 必填 -->
      <packages>扫描需要加密的包路径(多个以逗号隔开)</packages>
      <publicKey>公钥</publicPassword>
      <!-- 非必填 -->
      <password>自定义密码</password>
      <includeJars>需要加密的二方包(多个以逗号隔开)</includeJars>
      <excludeClasses>排除加密的类路径(多个以逗号隔开)</excludeClasses>
      <macCodes>指定Mac地址群(多个以逗号隔开)</macCodes>
  </configuration>
  <!-- end -->
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>xxxJarx</goal>
      </goals>
    </execution>
  </executions>
</plugin>

打包效果

最后打包效果如下:

image-20220412183228450

如果参数不全,则有提示

image-20220412183318592

最后

整个加密的流程逻辑已经讲清楚,按照这个思路其实你可以开发一个产品出来,不过这种产品使用的公司不多,但入局的人少,也可以是一个技术赛道。