啥,Tomcat里竟然还有特权应用?

开发 开发工具
在Tomcat部署的应用里,我们一般意义上都是指普通的应用,但对于一些特殊的应用,需要执行一些特别的操作,这就是我们今天提到的 Tomcat 内的特权应用(privileged app)。

在 Tomcat 部署的应用里,我们一般意义上都是指普通的应用,大家可以执行的操作等都是一样的,一条起跑线上的,没有区别。

但对于一些特殊的应用,需要执行一些特别的操作,比如官方默认提供的 manager应用,可以进行应用的启动、停止、部署等一系列操作。这些操作的背后,是 Tomcat默认包含的 ManagerServlet 实现的, manager 应用实现的,是对于该 Servelet 的调用。

那我们自己写一个应用去调用这个 ManagerServlet 不也一样可以吗?

答案是不行的,至少直接操作不行,需要配置。这里就是我们今天提到的 Tomcat 内的特权应用(privileged app)。

manager 应用就是自带的一个特权应用,因此它可以直接调用 ManagerServlet 进行一些容器内部的操作。

我们单独部署的其他应用,就会被限制调用这些容器提供的内部组件,比如一些 Servlet、Filter、Listener。

这些限制资源进行检查时,分为两种类型。

1. 一类是通过类型来进行判断,比如 Servlet 中,ManagerServlet、HTMLManagerServlet 这些都被称为ContainerServlet。实现了相同的接口。

2. 还有一类是在配置文件中指定的,有三个文件:

  • RestrictedFilters.properties
  • RestrictedListeners.properties
  • RestrictedServlets.properties

位于 org.apache.catalina.core 包中。

这些内部在 DefaultInstanceManager 初始化时进行解析,例如 servlet的内容解析后是这些:

 servlet的内容解析

这些内容也是容器提供的一些功能的实现,比如 GCI 请求处理, JMXProxyServlet 会将 JMX 的属性 dump 出来等等。

特权应用的配置

那怎样能让单独开发的应用成为特权应用,能调用这些容器的功能呢?

之前的文章介绍过应用的部署描述文件context.xml ( Tomcat目录部署与Context描述文件context.xml),这次我们的升级特权操作也是需要在这里进行。

具体来说,在应用部署时,将应用的 Context 对象中 privileged属性设置为 true。例如 manager 应用的配置是这样的,文件位于于 manager 的META-INF 目录中,单独开发时可以参考:

  1. <Context antiResourceLocking="false" privileged="true" > 

这样配置之后,在应用部署时就会将代表应用的 StandardContext 对象中 privileged 属性设置为true,后面会读取使用。

特权应用的工作原理

应用在启动时,会将 DefaultInstanceManager 做为其 instanceManager。该属性在后面 Servlet、Filter、Listener 这些组件加载时使用,做为实例管理器,进行 newInstance的管理。

以 Servlet 为例,有些 loadOnStartup 的 Servlet,会在部署启动应用时直接生成实例。

在 loadServlet 阶段时,会先通过 Wrapper 获取父容器 Context 的 instanceManager,再通过 instanceManager 来加载具体的 Servlet class。

以下为 instanceManager 进行 newInstace 时的逻辑

  1. public Object newInstance(String className)  { 
  2.        Class<?> clazz = loadClassMaybePrivileged(className, classLoader); 
  3.         return newInstance(clazz.newInstance(), clazz); 
  4.     } 

在loadClassMaybePrivileged中,对于catalina包中的 class,会使用ServerClassLoader 来进行加载,除了通过不同的classLoader加载外,还会进行上面说的 privileged 检查。

  1. private void checkAccess(Class<?> clazz) { 
  2.     if (privileged) { 
  3.         return; 
  4.     } 
  5.     if (Filter.class.isAssignableFrom(clazz)) { 
  6.         checkAccess(clazz, restrictedFilters); 
  7.     } else if (Servlet.class.isAssignableFrom(clazz)) { 
  8.         if (ContainerServlet.class.isAssignableFrom(clazz)) { 
  9.             throw new SecurityException("Restricted (ContainerServlet) " + 
  10.                     clazz); 
  11.         } 
  12.         checkAccess(clazz, restrictedServlets); 
  13.     } else { 
  14.         checkAccess(clazz, restrictedListeners); 
  15.     } 
  16. // 这里检查配置文件中的内容 
  17. private void checkAccess(Class<?> clazz, Properties restricted) { 
  18.     while (clazz != null) { 
  19.         if ("restricted".equals(restricted.getProperty(clazz.getName()))) { 
  20.             throw new SecurityException("Restricted " + clazz); 
  21.         } 
  22.         clazzclazz = clazz.getSuperclass(); 
  23.     } 

对于 priviledged 应用,之后的 checkAccess 就直接跳过,否则会判断是否是 ContainerServlet,是否在配置文件中进行限制等,只有都检查通过才可以进行 使用。

例如 普通的应用在使用HTMLManagerServlet ,此时由于限制检查,会提示500。

 

所以,之后有需要使用容器提供的功能时,可以将应用升级为特权应用,然后调用容器提供的高级功能。

【本文为51CTO专栏作者“侯树成”的原创稿件,转载请通过作者微信公众号『Tomcat那些事儿』获取授权】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2010-03-04 15:38:01

2020-06-17 07:56:19

前端存储数据

2009-07-02 17:59:51

2021-07-26 06:57:59

Synchronize线程安全

2021-03-05 18:36:59

日志Jar包代码

2019-07-01 10:48:13

Tomcat中间件TPM

2020-10-16 09:09:56

代码业务模型

2019-06-14 08:48:46

Tomcat日志SpringBoot

2021-03-04 09:50:23

Redis网络通信Redis服务器

2021-05-06 07:00:28

Excel数据技巧

2023-02-15 11:58:29

2017-03-01 11:15:43

LinuxKali LinuxSSH服务

2014-12-09 14:38:15

JavaScript6

2021-04-16 20:47:42

Go 指令函数

2020-09-23 16:05:42

Python手绘图表编程语言

2019-07-15 15:45:00

5.0Gs PCIe 4.0

2017-12-28 10:27:28

2018-03-08 10:24:48

Redis运维 RDR

2012-11-08 10:15:05

2022-03-03 07:00:43

Mybatiswhere标签
点赞
收藏

51CTO技术栈公众号