log4j 1.x 升级到 2.x 依赖不兼容问题的解决
Posted 不可描述的两脚兽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j 1.x 升级到 2.x 依赖不兼容问题的解决相关的知识,希望对你有一定的参考价值。
文章目录
1. 前言
log4j
出现了重大漏洞,黑客可以通过ldap
的注入漏洞而篡改后台代码,因而全球的技术部门几乎在同一时间进行着log4j
的升级。我司一个老产品用的是log4j 1.2.15
的版本,虽然并不受这次漏洞的影响但领导还是要求升级到最新的log4j 2.1.17
- 产品架构采用
jersey
作为web框架,再部署到tomcat
作为容器启动 - 直接替换jar,移除log4j 1.2.15版本的jar
log4j-1.2.15.jar
,增加新版本jar:log4j-1.2-api-2.17.1.jar
,log4j-api-2.17.1.jar
,log4j-core-2.17.1.jar
没错,log4j 2.x做了架构上的解耦因此有3个jar,而原来的1.x版本只有一个jar。这次的漏洞出现在core包里面,所以1.x本身不受这次漏洞的影响。 - 做完替换工作,尝试启动失败,抛出异常
java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.api.core.ScanningResourceConfig localhost-startStop-1 SEVERE: catch scan exception: (java.lang.ArrayIndexOutOfBoundsException: 52264)
java.lang.ArrayIndexOutOfBoundsException: 52264
at org.objectweb.asm.ClassReader.readClass(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:133)
at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)
at com.sun.jersey.core.util.Closing.f(Closing.java:71)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:92)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:79)
at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:35)
at com.sun.jersey.api.core.servlet.WebAppResourceConfig.init(WebAppResourceConfig.java:102)
at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:89)
at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:74)
at com.sun.jersey.spi.container.servlet.WebComponent.getWebAppResourceConfig(WebComponent.java:672)
at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:414)
at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:581)
at com.sun.jersey.spi.container.servlet.WebServletConfig.getDefaultResourceConfig(WebServletConfig.java:87)
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:703)
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)
at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1231)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1144)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1031)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4997)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5289)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1101)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1813)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
2. 解决
- 本着有问题先google的态度去搜索了一番。Stack Overflow上有人给出的答案是因为:
log4j-core-2.17.1.jar\\META-INF\\versions\\9
下的内容不能兼容导致,可以直接从jar内删除这部分代码。 - 按照此方法尝试后依然后异常抛出,于是想自己看下源码解决吧。
- 通过异常栈信息可以定位到抛异常的代码类
WebAppResourcesScanner
。
at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)
at com.sun.jersey.core.util.Closing.f(Closing.java:71)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:92)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:79)
- 找到jar中对应的类反编译查看
scan
方法。
private void scan(String root, final ScannerListener cfl)
Set<String> resourcePaths = this.sc.getResourcePaths(root);
if (resourcePaths != null)
Iterator i$ = resourcePaths.iterator();
while(i$.hasNext())
final String resourcePath = (String)i$.next();
if (resourcePath.endsWith("/"))
this.scan(resourcePath, cfl);
else if (resourcePath.endsWith(".jar"))
try
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure()
public void f(InputStream in) throws IOException
JarFileScanner.scan(in, "", cfl);
);
catch (IOException var9)
throw new ScannerException("IO error scanning jar " + resourcePath, var9);
else if (cfl.onAccept(resourcePath))
try
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure()
public void f(InputStream in) throws IOException
cfl.onProcess(resourcePath, in);
);
catch (IOException var7)
throw new ScannerException("IO error scanning resource " + resourcePath, var7);
catch (Exception var8)
this.LOGGER.log(Level.WARNING, "Failed to scan path: " + resourcePath, var8);
- 可以大概看到这部分的代码逻辑就是扫面传入的路径中所有的jar文件读取需要的配置,结合类名和代码基本可以判断出是为了加载web source的一些资源信息,而抛出异常的代码应该是在这部分
JarFileScanner.scan(in, "", cfl)
。
try
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure()
public void f(InputStream in) throws IOException
JarFileScanner.scan(in, "", cfl);
);
catch (IOException var9)
throw new ScannerException("IO error scanning jar " + resourcePath, var9);
- 可以看到这里只
catch (IOException var9)
,并没有catch到我们遇到的java.lang.ArrayIndexOutOfBoundsException
,这应该就是不兼容导致程序无法正常运行的直接原因。 - 于是我追加了异常捕获来处理异常。
try
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure()
public void f(InputStream in) throws IOException
JarFileScanner.scan(in, "", cfl);
);
catch (IOException var9)
throw new ScannerException("IO error scanning jar " + resourcePath, var9);
catch (Exception var10)
// 这里捕获到任何异常都打印日志,标明扫描失败的jar
this.LOGGER.log(Level.WARNING, "Failed to scan path: " + resourcePath, var10);
- 修改完后编译出
class
文件替换到jersey-servlet-1.10.jar
,将新的jersey更新到项目lib下。 - 重新部署运行,成功启动,并打印如下log.
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-api-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException)\\n java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-core-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException)\\n java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-1.2-api-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException: 14385)\\n java.lang.ArrayIndexOutOfBoundsException: 14385\\n at org.objectweb.asm.ClassReader.<init>(Unknown Source)\\n at org.objectweb.asm.ClassReader.<init>(Unknown Source)\\n at org.objectweb.asm.ClassReader.<init>(Unknown Source)\\n at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:133)\\n at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)\\n at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)\\n at com.sun.jersey.core.util.Closing.f(Closing.java:71)\\n at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:53)\\n at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:37)\\n at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:80)\\n at com.sun.jersey.api.core.servlet.WebAppResourceConfig.init(WebAppResourceConfig.java:102)\\n at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:89)\\n at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:74)\\n at com.sun.jersey.spi.container.servlet.WebComponent.getWebAppResourceConfig(WebComponent.java:672)\\n at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:414)\\n at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:581)\\n at com.sun.jersey.spi.container.servlet.WebServletConfig.getDefaultResourceConfig(WebServletConfig.java:87)\\n at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:703)\\n at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)\\n at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)\\n at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)\\n at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)\\n at javax.servlet.GenericServlet.init(GenericServlet.java:158)\\n at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1231)\\n at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1144)\\n at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1031)\\n at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4997)\\n at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5289)\\n at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)\\n at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)\\n at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)\\n at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)\\n at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1101)\\n at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1813)\\n at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)\\n at java.util.concurrent.FutureTask.run(FutureTask.java:266)\\n at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\\n at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\\n at java.lang.Thread.run(Thread.java:745)
- 这样的改动相当于在扫描web source的时候跳过了log4j的jar,这应该是没问题的,log4j里并不包含任何web项目的内容,只是一个打日志的工具包。
- 至此问题解决。
3. 后记
- 后来回想了一下,也许第一种google到的办法也许也可以解决。因为一共3个jar,而我只修改其中的一个,如果都处理的话也许是可以的,没有继续尝试。
- 修改的代码片段是用于扫描jar的,所以扫描路径肯定是可以配置的,我找到了对应的参数名,默认是扫描
/WEB-INF/lib/*
也就是所有jar,如果我通过参数去指定哪些jar需要被扫描也是可以的,但没这么做。这个项目不太了解,并不确定需要扫描哪些jar,如果我们所有的jar都加上会是很长的参数,而只为了跳过log4j实在是太麻烦了不易于维护。
以上是关于log4j 1.x 升级到 2.x 依赖不兼容问题的解决的主要内容,如果未能解决你的问题,请参考以下文章