Filter 过滤器

Filter 是拦截Request请求的对象:在用户的请求访问资源前处理 ServletRequest 以及 ServletResponse。

Filter 可用于日志记录、加解密、Session检查、图像文件保护等。通过 Filter 可以拦截处理某个资源或者某些资源。

Filter 配置可以通过 Annotation 或者部署描述符(web.xml)来完成,但是,当一个资源或者某些资源需要被多个 Filter 所使用且他们的触发顺序很重要时,只能通过部署描述符(web.xml)来配置。

Filter API

Filter 相关的接口都在 javax.servlet 包中,包含:Filter、FilterConfig、FilterChain

Filter 接口  public interface Filter

Filter 的实现必须实现 javax.servlet.Filter 接口,该接口包含三个方法

default void init(FilterConfig filterConfig) throws ServletException  // 一般在应用开始时,容器会调用该方法初始化Filer,只调用一次。FilterConfig由容器传入init方法中;default表名该方法有默认实现
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException  
// 当Servlet容器每次处理Filter相关的资源时,会调用该Filter实例的doFilter方法。 default void destroy() // 一般在应用停止的时候,容器会调用该方法来销毁Filter;default表名该方法有默认实现

在 Filter 的 doFilter的实现中,最后一行需要调用 FilterChain 中的 doChain 方法。注意 Filter 的 doFilter 方法里的第3个参数,就是 filterChain 的实例:

filterChain.doFilter(request, response)

只用在 Filter 链条中最后一个 Filter 里调用的 FilterChain.doFilter(),才会触发处理资源的方法。如果在结尾处没有调用 FilterChain.doFilter()的方法,则该Request请求中止,后面的处理就会中断。

FilterChain 接口  public interface FilterChain

该接口只有一个方法 doFilter(request, response) ,该方法和Filter接口中的doFiler(request, response, filterChain)方法定义不一样。

void doFilter(ServletRequest request, ServletResponse response) throws java.io.IOException, ServletException  //

一般 filter 都是一个链,部署描述符(web.xml)中配置了几个就是几个,一个一个的连在一起: request --> filter 1 --> filter 2 --> filter 3 --> ... --> request resource 

chain.doFilter(request, reponse) 将请求转发给过滤器链的下一个filter,如果没有下一个filter,那就是请求的资源。

如果在filter中忘记最后的chain.doFilter(request, response),会导致无法访问到请求资源。

FilterConfig 接口  public interface FilterConfig

该接口有四个方法

java.lang.String getFilterName()  //
ServletContext getServletContext()  // 
java.lang.String getInitParameter(java.lang.String name)  // 返回指定参数
java.util.Enumeration<java.lang.String> getInitParameterNames()  // 返回参数名称的Enumeration对象,如果没有给这个Filter配置任何参数,则返回空的Enumeration

Filter 配置

当完成 Filter 的实现之后,就可以开始配置 Filter 了,步骤如下

(1)确认哪些资源需要使用这个 Filter 拦截处理

(2)配置 Filter 的初始化参数值,这些参数可以在 Filter 的 init 方法中读取到

(3)给 Filter 取一个名字。

注册 Filter

可以通过@WebFilter进行注册,也可以通过部署描述符(web.xml)进行注册。

但是当多个 Filter 应用到同一个资源,Filter的触发顺序将变得非常重要,才是只能使用部署描述符来管理 Filter,即指定哪个 Filter 先触发。

使用@WebFilter需要熟悉下面的参数,这些参数都是在 javax.servlet.annotation.WebFilter 中定义的,所有参数都是可选的

asyncSupported   // boolean
description      // java.lang.String
dispatcherTypes  // DispatcherType[]
displayName      // java.lang.Sring
filterName       // java.lang.String
initParams       // WebInitParam[]
largeIcon        // java.lang.String
servletNames     // java.lang.String[]
smallIcon        // java.lang.String
urlPatterns      // java.lang.String[]
value            // java.lang.String[]

使用@WebFilter的例子

@WebFilter (filterName = "Security Filter", urlPatterns = { "/*" },
   initParams = {
     @WebInitParam(name = "frequency", value = "1909"),
     @WebInitParam(name = "resolution", value = "1024")
   }
)

使用部署描述符(web.xml)的例子

<filter>
  <filter-name>Security Filter</filter-name>
  <filter-class>filterClass的全限定名</filter-class>
  <init-param>
    <param-name>frequency</param-name>
    <param-value>1909</param-value>
  </init-param>
  <init-param>
    <param-name>resolution</param-name>
    <param-value>1024</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>Security Filter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

利用部署描述符配置 Filter 的触发顺序,例如 Filter 1 需要在 Filter 2 之前被触发,那么在部署描述符中,Filter 1 需要配置在 Filter 2 之前,如下

<filter>
  <filter-name>Filter 1</filter-name>
  <filter-class>实现Filter接口的类的全限定名</filter-class>
</filter>
<filter>
  <filter-name>Filter 2</filter-name>
  <filter-class>实现Filter接口的类的全限定名</filter-class>
</filter>
<fitler-mapping>
  ...
<filter-mapping>

实例一  日志 Filter

package app09a.filter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;

@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 appPath = filterConfig.getServletContext().getRealPath("/");  // 获取真实路径值,此处为“C:\Program Files\Java\apache-tomcat-9.0.12\webapps\app09a\”
        try {
            logger = new PrintWriter(new File(appPath, logFileName));  // 新建文件和PrintWriter对象
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        }
    }
    @Override
    public void destroy() {
        System.out.println("destroying filter");
        if (logger != null) {
            logger.close();
        }
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("LoggingFilter.doFilter");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        logger.println(new Date()+ " " + prefix + httpServletRequest.getRequestURI());
        logger.flush();
        filterChain.doFilter(request, response);  // 
    }
}
<!-- test.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
    This is a test for filter.
</body>
</html>

实例二 图像文件保护 Filter

package app09a.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter(filterName = "ImageProtectorFilter", urlPatterns = { "*.png", "*.jpg", "*.gif" })
public class ImageProtectorFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String referer = httpServletRequest.getHeader("referer");  // HTTP referer 属性是 header 的一部分,当浏览器向web服务器发送请求的时候,一般会带上 referer,告诉服务器我是从哪个页面链接过来的。
        System.out.println("referer: " + referer);                 // 直接访问图片时,referer 的值为 null
        if ( referer != null) {                                    // 通过JSP页面访问图片时,referer 的值为 http://localhost:8080/app09a/image.jsp
            filterChain.doFilter(request, response);               
        } else {                                                   
            throw new ServletException("Image not available!");
        }
    }
}
<!-- image.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>image</title>
</head>
<body>
    <img src='image/logo.jpg' />  <!-- 当浏览器通过该链接获取图片资源时,他也将该页面的 URL 作为 Header 的 referer 值传到服务器中 -->
</body>
</html>

实例三 下载基数 Filter

package app09a.filter;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter(filterName = "DownloadCounterFilter", urlPatterns = { "/*" })
public class DownloadCounterFilter implements Filter {
    ExecutorService executorService = Executors.newSingleThreadExecutor();  // 单线程池
    Properties downloadLog;  // Properties类对象主要用于读取Java的配置文件
    File logFile;
    
    @Override
    public void destroy() {
        executorService.shutdown();  // 关闭线程池
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String appPath = filterConfig.getServletContext().getRealPath("/");  // 获取该应用的绝对路径
        logFile = new File(appPath, "downloadLog.txt");
        if (!logFile.exists()) {
            try {
                logFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        downloadLog = new Properties();
        try {
            downloadLog.load(new FileReader(logFile));  // 从文件中载入
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        final String uri = httpServletRequest.getRequestURI();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                String property = downloadLog.getProperty(uri);
                if (property == null) {
                    downloadLog.setProperty(uri, "1");
                } else {
                    int count = 0;
                    try {
                        count = Integer.parseInt(property);
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                    }
                    count ++;
                    downloadLog.setProperty(uri, Integer.toString(count));
                }
                try {
                    downloadLog.store(new FileWriter(logFile), "");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        filterChain.doFilter(request, response);
    }
}

 

posted on 2018-11-05 00:21  0820LL  阅读(233)  评论(0编辑  收藏  举报

导航