Servlet--Filter
Filter 是拦截Request 请求的对象:在用户的请求访问资源前处理ServletRequest 以及 ServletResponse,它可用于日志记录,加解密,Session检查,图像文件保护等。通过Filter可以拦截处理某个资源或者某些资源。Filter 的配置可以通过Annotation 或者部署描述来完成。当一个资源或者某些资源需要被多个Filter所用到,且它的触发顺序很重要时,只能通过部署描述来配置。
Filter API
Filter 的实现必须继承javax.servlet.Filter 接口。这个接口包含了Filter 的三个生命周期:init , doFilter , destroy 。
Servlet容器初始化Filter时,会触发Filter的init 方法,一般来说是在应用开始时。也就是说,init 方法并不是在该Filter 相关的资源使用到时才初始化的,而且这个方法只调用一次,用于初始化Filter 。
当Servlet 容器每次处理Filter 相关资源时,都会调用该Filter 实例的doFilter 方法。Filter 的doFilter 方法包含 ServletRequest,ServletResponse,FilterChain 这三个参数。
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
这意味着,doFilter 的实现中,允许给ServletRequest 增加属性或者Header。当然也可以修饰 ServletRequest或者 ServletResponse 来改变它们的行为。
在Filter 的 doFilter 的实现中,最后一行需要调用FilterChain 中的doChain 方法。如果没有调用,则该Request 请求会中止,后面的处理就会中断。
destroy 方法在 Servlet 容器要销毁Filter 时触发,一般在应用停止的时候进行调用。
除非Filter 在部署描述中被多次定义到,否则Servlet 窗口只会为每个 Filter 创建单一实例。由于 Servlet / JSP 的应用通常要处理用户并发请求,此时 Filter 实例需要同时被多个线程多关联到,因此需要非常小心地处理多线程问题。
Filter 配置
需如下步骤:
- 确认哪些资源需要使用这个Filter 拦截处理
- 配置Filter 的初始化参数值,这些参数可以在Filter 的 init 方法中读取到。
- 给Filter 取一个名称。一般来说,这个名称没有什么特别的含义,但在一些特殊的情况下,这个名字十分有用。例如,要记录Filter 的初始化时间,但这个应用中有许多的 Filter ,这时它就可以用来识别 Filter 了。
FilterConfig 接口允许通过它的getServletContext 的方法来访问ServletContext;
如果配置了 Filter 的名字,在 FilterConfig 的 getFilterName 中就可以获取 Filter 的名字。
当然,最重要的还是获取到开发者或者运维给Filter 配置的初始化参数。为了获取这些初始化参数,需要用到FilterConfig 中的两个方法,
第一个方法是getParameterNames:
Enumeration<String> getInitParameterNames();
返回的是 Enumeration 对象。如果没有给这个Filter 配置任何参数,该方法返回的是空的Enumeration 对象。
第二个方法是 getParameter:
String getInitParameter(String var1);
有两种方法配置 Filter: 一种通过 WebFilter 的 Annotation 来配置 Filter,另一种就是通过部署描述来注册。使用 @WebFilter 的方法,只需要在 Filter 的实现类中增加一个注解即可,不需要重复地配置部署描述。当然,此时要修改配置参数,就需要重新构建Filter 实现类。换句话说,使用部署米烤熟意味着修改Filter 配置只要修改一下文本文件就可以了。
使用 @WebFilter ,选哟熟悉下列参数:
属性 | 描述 |
---|---|
asyncSupported | Filter是否支持异步操作 |
description | Filter 的描述 |
despatcerTypes | Filter 所生效范围 |
displayName | Filter 的显示名 |
filterName | Filter 的名称 |
initParams | Filter 的初始化参数 |
largeIcon | Filter 的大图名称 |
servletName | Filter 所生效的Servlet 名称 |
urlPatterns | Filter 所生效的URL 路径 |
value | Filter 所生效的URL 路径 |
smallIcon | Filter 的小图名称 |
例子:
使用WebFilter 部署:
@WebFilter(filterName = "filterdemo",urlPatterns = {"/*"})
使用部署描述:
<filter>
<filter-name>filterdemo</filter-name>
<filter-class>Filter.com.FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>filterdemo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
描述初始化参数的配置:
@WebFilter(filterName = "filterdemo",urlPatterns = {"/*"},
initParams = {
@WebInitParam(name = "a",value = "1"), @WebInitParam(name = "b",value = "2")
})
部署描述:
<filter>
<filter-name>filterdemo</filter-name>
<filter-class>Filter.com.FilterDemo</filter-class>
<init-param>
<param-name>a</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>b</param-name>
<param-value>2</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filterdemo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
示例1:日志Filter
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
@WebFilter(filterName = "loggingFilter",urlPatterns = {"/*"},
initParams = {
@WebInitParam(name = "logFileName",value = "log.txt"),@WebInitParam(name = "prefix",value = "URI:")
}
)
public class LoggingFilter implements Filter {
private PrintWriter logger;
private String prefix;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
prefix = filterConfig.getInitParameter("prefix");
String logFileName = filterConfig.getInitParameter("logFileName");
String realPath = filterConfig.getServletContext().getRealPath("/");
//wtihout path info in logFileName ,the lgo file will be created in $TOMCAT_HOME/bin
System.out.println("logFile:" + realPath+logFileName);
try {
logger = new PrintWriter(new File(realPath,logFileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("loggingFilter.doFilter");
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
logger.println(new Date()+" "+prefix+httpServletRequest.getRequestURI());
logger.flush();
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("filter is destroying");
if (logger != null){
logger.close();
}
}
}
随机发出一个请求:
如之前的:http://localhost:8080/countries.jsp,
可以看到log.txt 访问记录:
示例2:图像文件保护Filter
原理:
为了防止下载图像文件。应用中的图像文件只有当图像连接在页面中被点击的时候才会显示。该Filter 的实现原理是检查 HTTP Header 的 referer 值。如果该值为 null ,就意味着当前的请求中没有 referer 值,即当前的请求是直接通过输入 URL 来访问该资源的。
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(filterName = "iamgeprotectorFilter",urlPatterns = {"*.png","*.jpg","*.gif"})
public class ImageProtectorFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("ImageProtectorFilter");
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String referer = httpServletRequest.getHeader("referer");
System.out.println("referer:" + referer);
if (referer != null){
filterChain.doFilter(servletRequest,servletResponse);
}else{
throw new ServletException("Image not available");
}
}
@Override
public void destroy() {
}
}
运行结果:
image.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>图像Filter demo</title>
</head>
<body>
<img src="/img/ysn.jpg">
</body>
</html>
直接访问该资源地址,则:
Filter 顺序
如果多个 Filter 应用于同一个资源,Filter 的触发顺序将变得非常重要,这时就需要使用部署描述来管理Filter :指定哪个Filter 先被触发。例如:Filter 1 需要在 Filter 2 前被触发,那么在部署描述中,Filter 1 需要配置在Filter 2 之前。
通过部署描述之外的配置来指定Filter 触发的顺序是不可能的。