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 访问记录:

Servlet--Filter

示例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>

Servlet--Filter

直接访问该资源地址,则:

Servlet--Filter

Filter 顺序

如果多个 Filter 应用于同一个资源,Filter 的触发顺序将变得非常重要,这时就需要使用部署描述来管理Filter :指定哪个Filter 先被触发。例如:Filter 1 需要在 Filter 2 前被触发,那么在部署描述中,Filter 1 需要配置在Filter 2 之前。

通过部署描述之外的配置来指定Filter 触发的顺序是不可能的。