前言
项目需要新集成一个sdk进行服务调用,在本地使用 Tomcat 开发调试好好的,更新到服务器上发现不行了,服务都被起不来了。报错 SecurityException: JCE cannot authenticate the provider BC
。
更换和东方通一样的版本jar后,又报错 java.lang.SecurityException class "org.bouncycastle.crypto.digests.GeneralDigest"'s signer information does not match signer information of other classes in the same package
,版本太低又不满足sdk的使用
解决思路
在尝试不同的解决方法后
方式 | 结果 |
---|---|
项目依赖的jar进行降级 | 项目报错 |
置换东方通的jar | 各种奇葩错 |
对sdk依赖的jar版本进行降级到与东方通的jar版本一致 | 修改量比较大 |
同时也看到使用maven打包方式解决 【复盘】bcprov-jdk16包冲突问题(不同版本jar兼容) 以及 maven-shade-plugin的使用 ,可惜我用不了。同时给我提供了一个思路:更换包名
思路有了那就网上看看有没有好用的工具,最终发现一个打包替换工具:jarjar.jar
通过jarjar.jar来替换JAR包名的详细介绍
- 获取jar
https://mvnrepository.com/artifact/com.googlecode.jarjar/jarjar这个jar版本比较老,针对有注解的jar包不能处理,java8的函数特性也不能有效处理
https://mvnrepository.com/artifact/com.eed3si9n.jarjar/jarjar-assembly/1.13.1版本比较新,使用规则和较老的jarjar比较像,放心使用
使用java -jar jarjar-1.3.jar
可以查看帮助
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads/testtt
$ java -jar jarjar-1.3.jar
Jar Jar Links - 一个重新打包和嵌入Java库的工具
Copyright 2007 Google Inc.
命令行用法:
java -jar jarjar.jar [help]
打印此帮助消息。
java -jar jarjar.jar strings <cp>
转储类路径<cp>中的所有字符串字面值。如果类具有调试信息,则将包含行号。
java -jar jarjar.jar find <level> <cp1> [<cp2>]
打印类路径<cp1>中对类路径<cp2>的依赖项。如果省略<cp2>,则两个参数都使用<cp1>。
level参数必须是"class"或"jar"。前者打印各个类之间的依赖关系,
而后者仅打印 jar->jar 依赖关系。这里的"jar"实际上是任何类路径组件,
可以是jar文件、zip文件或父目录(见下文)。
java -jar jarjar.jar process <rulesFile> <inJar> <outJar>
转换jar文件,将新的jar文件写入。任何以。命名的现有文件将被删除。
转换是由rules参数指定的文件中的一组规则定义的(见下文)。
类路径的格式:
classpath参数是目录、jar文件或zip文件的冒号或分号分隔集(取决于平台)。有关更多详细信息,请参阅以下页面:
http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/classpath.html
Mustang-style 通配符支持:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6268383
规则文件格式:
规则文件是一个文本文件,每行一个规则。前导和尾随空格将被忽略。有三种类型的规则:
rule <pattern> <result>
zap <pattern>
keep <pattern>
标准规则("rule")用于重命名类。所有对重命名类的引用也将被更新。
如果一个类名与多个规则匹配,则只应用第一个规则。
<pattern> 是带有可选通配符的类名。 "**" 将匹配任何有效的类名子字符串。为了匹配单个
包装组件(从匹配中排除"."), 可以使用单个"*"代替。
<result> 是一个类名,它可以选择性地引用由通配符匹配的子字符串。对<pattern>中的每个"*"或"**"都有编号引用,从左到右:"@1", "@2",等。
一个特殊的"@0"引用包含了整个匹配的类名。
"zap" 规则将导致从生成的jar文件中删除任何匹配的类。在重命名规则之前处理所有zap规则。
"keep" 规则将所有匹配的类标记为"roots"。如果定义了任何keep规则,那么在编写输出jar时,
不能通过依赖分析从根目录访问的所有类都将被丢弃。这是重命名和删除之后的最后一步。
使用jarjar修改sdk和bcprov等jar
编写rule.txt
rule org.bouncycastle.** shade.bouncycastle.@1
替换 package 和 import
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads/testtt
$ java -jar jarjar-1.3.jar process rule.txt bcprov-jdk15to18-1.67.jar shade-bcprov-jdk15to18-1.67.jar
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads/testtt
$ java -jar jarjar-1.3.jar process rule.txt bcpkix-jdk15to18-1.67.jar shade-bcpkix-jdk15to18-1.67.jar
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads/testtt
$ java -jar jarjar-1.3.jar process rule.txt send-api-sdk-1.2.1.jar newsend-api-sdk-1.2.1.jar
最终反编译结果如下
可以发现对package
,import
都进行了替换。将替换后的sdk和bcprov给替换到线上,原来冲突的jar给删除,服务正常,到此完结撒花~~
拓展:如果有个性化的修改也可以使用增量更新的方式(一般用不到,此处只是抛砖引玉)
注意:如果只是A.jar依赖B.jar 只是想 A和B的依赖绑定,使用上面的方法对 A.jar 和 B.jar 都修改就可以满足需求,下面的个性化修改场景是,不光对依赖有修改,其中的逻辑也有修改,可以利用 java 同包同名类覆盖的特性进行某个 class 重新编译。
替换原来sdk的jar中class调用替换包名后的加密JAR,具体可以移步《Java项目中 Jar 包增量更新办法,解压修改 Jar 包中的文件后重新打成 Jar 包》
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads
$ cd aaaaaaa/
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads/aaaaaaa
$ ll
total 60
-rw-r--r-- 1 houzw 197121 58060 Oct 7 17:29 send-api-sdk-1.2.1.jar
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads/aaaaaaa
$ jar -xvf send-api-sdk-1.2.1.jar
▒Ѵ▒▒▒: META-INF/
▒ѽ▒ѹ: META-INF/MANIFEST.MF
.
.
.
houzw@DESKTOP-J9FIROU MINGW64 ~/Downloads/aaaaaaa
$ jar -uvf0 send-api-sdk-1.2.1.jar cn
保留替换的class,其他无关的文件都删除掉,执行 jar -uvf0 xxx.jar文件 文件夹
使用文件夹对jar文件进行增量更新。
更新完后反编译看一下,确实是调用新的方法。
将线上服务再次启动,就没有发现报错了,服务调用也正常。完事儿,撒花~~
总结
当有jar包冲突。且出现了既要又要的情况,需要兼容不同版本的jar,无论是maven方案还是自己修改jar 等都是规避jvm的双亲委派机制,层级越高的类加载器越先加载其加载路径下的类。如maven最短依赖路径、自己修改包名等。
所以当上述既要又要的情况,那就将按版本进行包名和import替换无疑是比较好的解决方法,只需要执行命令行一个全新的一套jar就完成了。jarjar.jar
无疑也是首要选择
最后祝大家玩的开心!!!