Tomcat内存马:基本知识

Uncategorized
1.6k words

Tomcat架构浅析

基本架构如图所示,咱们主要把关注点放在ConnectorContainer这两个组件这里。

Connector 组件 ,相当于tomcat的外交大臣,把浏览器传来的字节流封装处理成ServletRequest 对象,之后再把ServletRequest 对象 转发给Contaniner组件处理。当Container 组件处理完毕之后,Contaniner 组件会给Connector组件返回一个 ServletResponse对象,该对象再反过来经由Connector组件一系列处理,还原成字节流,最终把返回结果转发给浏览器,让浏览器进行页面渲染。

过程挺简单的,但显然不是今天的重点,我们内存马主要还是依靠这里的Container组件来实现的,所以我们再跟进分析一下Container组件的运行机制,大体如下图所示。
Container 组件又称 Catalina,下文存在一定的混用,别混淆了)

这里面从Engine到Wrapper涉及到了几个细分概念,我们先不急着解释,可以先从我下面偷的
这张图来感受一下他们之间的层次关系。

能够看出来层次关系大致是遵循树形结构的(wrapper和servlet肯定不是,人家就是一对一的),从这张图的边角注释基本也能猜出来,各个容器的职能,但还是来做一下具体解释。


  • Engine:表示整个 Catalina 的 Servlet 引擎,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine,但是一个引擎可包含多个 Host
  • Host:代表一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可包含多个 Context
  • Context:表示一个 Web 应用程序,每一个Context都有唯一的path,一个Web应用可包含多个 Wrapper
  • Wrapper:表示一个Servlet,负责管理整个 Servlet 的生命周期,包括装载、初始化、资源回收等

以上就是关于Connector组件中子容器的介绍,但我们重新回到那个图里面,我们仍然可以发现子容器之中亦有迷雾尚未解开,就比如每个容器都出现的那个 Pipeline 我们就不清楚它到底是什么东西。这里就要再介绍一下Tomcat 的 阀和管道 机制。

首先什么是管道?我们可以先看一下接口的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface Pipeline extends Contained {  
    public Valve getBasic();
    public void setBasic(Valve valve);
    public void addValve(Valve valve);
    public Valve[] getValves();
    public void removeValve(Valve valve);
    public void findNonAsyncValves(Set<String> result);
}
//上面是管道
public interface Valve {
    public Valve getNext();
    public void setNext(Valve valve);
    public void backgroundProcess();
    public void invoke(Request request, Response response) throws IOException, ServletException;
    public boolean isAsyncSupported();
}
//上面是阀

可以看出来,阀是由管道所调度掌控的,一个管道可以拥有多个阀,但管道并不直接实现功能,而是由阀所掌控的阀来实现具体功能(就是Valve#invoke)这里来实现。他们的运行机制有点类似于FilterChain那种责任链机制,层层外包,通力协作,当一个管道所有的阀的被调度完成,则最后一个阀可以调度别的管道的首阀开始新一轮的外包协作。

(这个图略了点,只有一个管道,稍微抽象点,但毕竟图是偷的,凑合着看吧)

Tomcat中三个Context

  • ServletContext

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    package javax.servlet;  
    ...
    public interface ServletContext {
    String TEMPDIR = "javax.servlet.context.tempdir";
    String ORDERED_LIBS = "javax.servlet.context.orderedLibs";
    String getContextPath();
    ServletContext getContext(String var1);
    Servlet getServlet(String var1) throws ServletException;
    Enumeration<Servlet> getServlets();
    Enumeration<String> getServletNames();
    void log(String var1);
    void log(Exception var1, String var2);
    void log(String var1, Throwable var2);
    String getRealPath(String var1);
    String getServerInfo();
    String getInitParameter(String var1);
    Enumeration<String> getInitParameterNames();
    boolean setInitParameter(String var1, String var2);
    Object getAttribute(String var1);
    Enumeration<String> getAttributeNames();
    void setAttribute(String var1, Object var2);
    void removeAttribute(String var1);
    String getServletContextName();
    Dynamic addServlet(String var1, String var2);
    Dynamic addServlet(String var1, Servlet var2);
    Dynamic addServlet(String var1, Class<? extends Servlet> var2);
    Dynamic addJspFile(String var1, String var2);
    <T extends Servlet> T createServlet(Class<T> var1) throws ServletException;
    ServletRegistration getServletRegistration(String var1);
    Map<String, ? extends ServletRegistration> getServletRegistrations();
    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2);
    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2);
    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class<? extends Filter> var2);
    <T extends Filter> T createFilter(Class<T> var1) throws ServletException;
    FilterRegistration getFilterRegistration(String var1);
    Map<String, ? extends FilterRegistration> getFilterRegistrations();
    SessionCookieConfig getSessionCookieConfig();
    void setSessionTrackingModes(Set<SessionTrackingMode> var1);
    Set<SessionTrackingMode> getDefaultSessionTrackingModes();
    Set<SessionTrackingMode> getEffectiveSessionTrackingModes();
    void addListener(String var1);
    <T extends EventListener> void addListener(T var1);
    void addListener(Class<? extends EventListener> var1);
    <T extends EventListener> T createListener(Class<T> var1) throws ServletException;
    JspConfigDescriptor getJspConfigDescriptor();
    ClassLoader getClassLoader();
    void declareRoles(String... var1);
    String getVirtualServerName();
    int getSessionTimeout();
    void setSessionTimeout(int var1);
    String getRequestCharacterEncoding();
    void setRequestCharacterEncoding(String var1);
    String getResponseCharacterEncoding();
    void setResponseCharacterEncoding(String var1);
    }

    当我们使用Tomcat这样的Java Web服务器时,它提供了一个特殊的对象叫做ServletContext(Servlet上下文)。可以把ServletContext看作是一个容器,它存储了与Web应用程序相关的信息和功能。通俗来说,ServletContext就像是整个Web应用程序的大本营。它保存了所有在同一个Web应用程序中运行的Servlet共享的数据、配置和资源。可以把它看作是一个大家庭的家长,负责协调和管理每个Servlet的需求,并提供它们需要的支持。我们所有的Servlet都得对其进行实现。

  • ApplicationContext
    在Tomcat中,ServletContext接口的具体实现就是ApplicationContext类,其实现了ServletContext接口中定义的一些方法。

  • StandardContext
    org.apache.catalina.core.StandardContext是子容器Context的标准实现类,其中包含了对Context子容器中资源的各种操作。也是我们内存马这里真正起到作用的类。

总结以下大致如下

引用下别的师傅的话

1
ServletContext接口的实现类为ApplicationContext类和ApplicationContextFacade类,其中ApplicationContextFacade是对ApplicationContext类的包装。我们对Context容器中各种资源进行操作时,最终调用的还是StandardContext中的方法,因此StandardContext是Tomcat中负责与底层交互的Context。

三大组件加载顺序

三者的加载顺序为Listener->Filter->Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
try {
// Start manager
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
// Start ContainerBackgroundProcessor thread
super.threadStart();
}if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
try {
// Start manager
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
// Start ContainerBackgroundProcessor thread
super.threadStart();
}

...

参考链接