在多个<host>元素之间共享通用库的单个Classloader是否安全?

时间:2019-04-11 15:48:06

标签: java spring tomcat log4j c3p0

这是我在Tomcat中的server.xml文件的摘录:

<Host name="client9001.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
<Host name="client9002.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
<Host name="client9003.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
...
<Host name="client9254.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>

众所周知,每个Host元素都会创建自己的WebappClassloader实例,这使得每个主机都完全隔离且安全。通过此设置,我可以安全地使用一个Tomcat实例为X个客户提供服务,每个客户都在不同的子域下提供服务。这种设置可以防止客户B的行为操纵客户A的对象。

但是,这会产生对非堆空间的不可扩展的需求(因为每个类都被加载X次),我想知道是否可以为所有主机创建一个共享的类加载器。我知道Tomcat支持这种事情(使用https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html#Class_Loader_Definitions中的“ Common”类加载器),但是我的问题是这样做是否安全。我的意思是,我可以通过消除所有静态字段来确保自己的类安全,但这足够吗?我担心第3方库,例如Spring框架,Log4J,Joda-time以及我的产品代码随附的其他几个库。

我已经使用反射来标识那些类中的所有静态变量,并且我发现了252个静态非最终字段和5772个静态非最终字段,这些都在第三方代码中(我自己代码只有3个字段我将消除)。

我应该走这条路来回顾所有这6027个字段吗?我的意思是,这是个坏主意吗?如果是这样,那么“通用”类加载器中可以共享哪种代码?

是否有充分的证据表明所有这些著名的框架都可以像这样安全地共享,否则我应该将所有内容视为不安全,直到被证明是其他方式?

1 个答案:

答案 0 :(得分:0)

我会非常谨慎...并确定性能是否值得冒险

我正在考虑一种假设的情况,在这种情况下,使用共享的ClassLoader可能会带来一些意想不到的后果...

我看过Log4J,它通过LogManager创建记录器,该记录器扩展了java.util.logging.LogManager,用于维护关于记录器和日志服务的一组共享状态。

想象一下,这些客户端执行private static final Logger LOGGER = LogManager.getLogger();为每个主机创建记录器的情况

 <Host name="client9001.example.com">  
 <Host name="client9002.example.com">
 <Host name="client9003.example.com"> 

 <Host name="BADclient.example.com"> 

由于LogManager在所有主机之间都是静态共享的,因此错误的客户端可以调用LogManagers.getLoggerNames();,这将向该静态LogManager实例返回所有已注册记录器的列表,并且会泄漏有关在同一tomcat实例上运行的其他客户端的信息。 /能够删除使用同一类的其他主机的事件监听器(**我没有尝试过**)

基本上,您希望放置将在JVM上运行的所有主机(以及将来的所有主机)之间100%共享的代码。常见的示例是初始化JDBC连接。

此外,在代码维护方面,如果您打算钻研一下,并尝试查看所有静态方法/字段..每次更新第三方输入时,您都必须再次对其进行检查,以确保什么也没有介绍...甚至可能审查了第三方进口的进口

当他询问在Tomcat中使用通用类加载器的危险时,Tomcat classloader violates delegating policy可能会引起这个问题的困扰