破坏双亲委托机制的一些情况---Tomcat和JDBC,破坏后的安全问题

采用双亲委托机制的原因

  类加载器就是将字节码搬进jvm方法区的组件。我们知道,JVM识别加载进来的类是通过类加载器+类全名完成的,也就是说同一个类由不同类加载器加载进去的话就会被视为不同的类。jdk提供的类加载器及他们的关系如下:

我们可以自己继承ClassLoader定义多个自己的类加载器,但是这多个类加载器去加载同一个类(例如java.lang.Integer)就会多次加载这个类,jvm也会将这两次加载的Integer类看成不同的类,因为加载器不是同一个。而我们希望的是,对于这种核心基础类,只需加载一个就行了。此外,我们甚至可以自定义一个Integer类,用自定义的加载器加载进去,这对于程序来说是不安全的。

基于以上两个原因,Java采用了双亲委托机制。工作流程:

  1. 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有加载缓存,当一个类被加载了以后就会放入缓存,下次加载的时候就可以直接返回。

  2.  当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父的父去加载,一直到BootStrapClassLoader.

  3.  到BootStrapClassLoader都没有发现缓存的话,就开始主动加载。(加载成功将其放入它自己的缓存中,以便下次有加载请求的时候直接返回)

  4. 如果BootStrapClassLoader在自己负责加载的文件夹中找不到目标类,则将任务交给下一层主动加载,不成功继续交给下一层主动加载。

      总结:先层层往上找有没有缓存,到顶后还没缓存就主动加载,失败就层层往下尝试。

因此使用此机制原因归为:

  1. 使类加载器有优先关系,防止了重复加载。父类加载了子类便不会去加载。例如:核心的基础类只会被引导类加载器加载,保证了核心基础类在jvm的唯一性即一个类总是由一个类加载器加载

  2. 更安全。冒充核心类名的类最终总是到引导类记载器。保证了核心基础类的正确性即只会加载指定包中唯一的类

 

破坏双亲委托机制的情形

  双亲委托机制并不是强制使用的,我们可以继承java.lang.ClassLoader类,实现自己的类加载器。如果想保持双亲委派模型,就应该重写findClass(name)方法;如果想破坏双亲委派模型,可以重写loadClass(name)方法。

  下面例举破坏双亲模型的情形。

1. Tomcat

  Tomcat是一个servlet容器,他也是Java编写的,需要运行在JVM之上。我们编写的服务端程序即servlet部署在tomcat中才是完整的服务端程序(tomcat完成了与客户端通信、管理servlet生命周期等功能)。一般生产环境下,一个Tomcat中会运行着多个servlet,这个时候我们就不应该使用双亲模型了。原因是多个servlet应该是完全隔离的,而使用双亲模型,多个服务器程序就是共用一套类库了,这样各种错误都来了。

  正确的做法是,每个部署在tomcat中的servlet都有自己的WebAppClassLoader,加载类时直接使用它加载,这样各个servlet加载的类互不干涉。

  一个Tomcat启动会伴随一个JVM进程的启动(因为Tomcat也是Java编写的),多个部署在Tomcat的应用可以互不干涉的运行在这一个JVM中,正式因为破坏了双亲委托机制。

2. JDBC

  使用JDBC建立数据库连接:

Connection con = DriverManager.getConnection(url , username , password ) ; 

JDBC的Driver接口定义在JDK中,但是其具体实现由各个数据库的服务商来提供,比如MySQL驱动包。

DriverManager 类位于 $JAVA_HOME中jre/lib/rt.jar 包,由BootStrap类加载器加载,而其具体Driver实现类是位于服务商提供的 Jar 包。根据类加载机制,当被装载的类引用了另外一个类的加载时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类,也就是说BootStrap类加载器还要去加载jar包中的Driver接口的实现类,但BootStrap类加载器默认只负责加载 $JAVA_HOME中jre/lib里的class,所以又需要由子类加载器去加载Driver实现,这就破坏了双亲委派模型。

 

实际的Tomcat类加载机制

  既然Tomcat破坏了双亲模型,那么是否表示他可能会装载到自定义的有害的基础类呢?先看看Tomcat类加载器的结构(图片来源于网络https://www.cnblogs.com/aspirant/p/8991830.html):

  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  • WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;

每个应用在部署后,都会创建一个唯一的类加载器,即自己的WebAppClassLoader,该类加载器会加载的类仅仅对本应用可见。Web应用程序的WebApp类加载器加载类要加载某类时,该类加载器将首先查找自己负责的文件夹中是否存在此类,而不是向父加载器委派。也有例外,属于JRE基类的类不能如此,还是要走双亲委派。

  综上,Tomcat也有一套层次结构的类加载器,加载JRE基类时还是走双亲委派,其他就是优先使用WebApp加载器加载。因此,即使破坏了双亲模型,Tomcat类加载也不会有安全问题。

posted on 2019-10-10 17:39  千山万水楼外楼  阅读(988)  评论(0编辑  收藏  举报

导航