关于过滤器

105 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

介绍

在项目开发中我们往往需要考虑用户访问时是否有权限进行访问,而没有权限的用户我们就需要将它过滤掉,此时我们就需要使用到过滤器来帮助我们。过滤器可以动态地拦截请求和响应,可以对请求或响应中的信息做额外处理。

过滤器 Filter 的基本功能是对 Servlet 的调用过程进行干预,在 Servlet 处理请求及响应的过程中增加一些特定的功能。可以使用过滤器实现的功能有:URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息、统一编码方式等。

程序中的过滤器就好比生活中的自来水过滤器,过滤器原理如下图:

图片描述

当客户端向服务器中的资源发出请求时,会先被过滤器 Filter 拦截处理,之后再将处理后的请求转发给真正的服务器资源( JSP 或 Servlet 等)。此外服务器做出响应前,响应结果也会先被过滤器拦截处理,之后再将处理后的响应转发给客户端。

实现

程序中的过滤器,在语法上是实现了 javax.servlet.Filter 接口的类。javax.servlet.Filter 接口中定义了下表所示的三个方法。

方 法简 介
void init(FilterConfig conf)用于执行过滤器的初始化工作。Web 容器会在 Web 项目启动前,自动调用该方法。该方法类似于 Servlet 的 init() 方法。
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)当请求和响应被过滤器拦截后,就会交给 doFilter() 方法来处理:参数 request 就是拦截的请求对象,参数 response 就是拦截的响应对象,可以使用参数 chain 的 doFilter() 方法来将拦截的请求和响应放行。 该方法类似于 Servlet 的 doGet()doPost() 等方法。
void destroy()用于释放或关闭被 Filter 对象打开的资源,例如关闭数据库、关闭 IO 流等操作。在 Web 项目关闭时,由 Web 容器自动调用该方法。该方法类似于 Servlet 的 destroy() 方法。

与 Servlet 类似,Filter 的 init() 和 destroy() 方法都只会被调用一次,而 doFilter() 方法会在每次客户端发出请求时被调用。init() 方法里的 FilterConfig 参数,主要是为过滤器提供初始化参数。FilterConfig 是一个接口,提供了如下表所示的常用方法。

方 法简 介
String getFilterName()获取 web.xml 中的过滤器的名称。
String getInitParameter(String param)获取 web.xml 中参数名对应的参数值。
ServletContext getServletContext()获取 web 应用程序的 ServletContext。

实现第一个过滤器程序

1.创建项目

2.创建完成后,在项目中的 pom.xml 中写入以下配置:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.shiyan</groupId>
  <artifactId>ServletProject</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>ServletProject Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <!-- servlet 依赖 jar 包 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- jsp 依赖 jar 包 -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
  </dependencies>
  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
    <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <configuration>
                        <!-- 端口 -->
                        <port>8080</port>
                        <!-- 编码-->
                        <uriEncoding>UTF-8</uriEncoding>
                         <!-- 项目的启动路径-->
                        <path>/</path>
                         <!-- 项目名称 -->
                        <finalName>ServletProject </finalName>
                        <!-- 启动的命令名称-->
                        <server>tomcat7</server>
                    </configuration>
                </plugin>
            </plugins>
    </build>
</project>

3.在 webapp 下新建用于发送请求的客户端 JSP — index.jsp。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index</title>
</head>
<body>
    <a href="MyServlet">访问MyServlet…</a>
</body>
</html>

4.在 main 目录下新建 Java 代码所在的目录 java,并在该目录下新建 java 包 org/shiyan/servlet,并在该目录下新建用于处理请求的控制器 Servlet — MyServlet.java。

package org.shiyan.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doPost…");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doGet…");
    }
}

5.在 WEB-INF 目录下的 web.xml 中配置此 Servlet。

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>org.shiyan.servlet.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>
</web-app>

6.在 org/shiyan 包下新建 filter 包并新建 MyFirstFilter 类。

package org.shiyan.filter;

import java.io.IOException;
import javax.servlet.*;

public class MyFirstFilter implements Filter{

    public void init(FilterConfig arg0) throws ServletException{
        System.out.println("过滤器01的初始化init()方法…");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException{
        System.out.println("过滤器01的执行方法:doFilter()方法…");
    }

    public void destroy(){
        System.out.println("过滤器01的销毁destroy()方法…");
    }
}

7.在 web.xml 中新增以下配置注册 Filter。

<filter>
      <filter-name>MyFirstFilter</filter-name>
      <filter-class>
        org.shiyan.filter.MyFirstFilter
    </filter-class>
</filter>
<filter-mapping>
      <filter-name>MyFirstFilter</filter-name>
      <url-pattern>/MyServlet</url-pattern>
</filter-mapping>

8.运行

控制台结果如图:

图片描述

图片描述

可以发现 index.jsp 通过超链接向 Servlet 发出的请求确实被 Filter 拦截了,并且在 Tomcat 服务启动的过程中 init() 方法就已经自动执行了。

修改

本次只执行了 Filter 中的 doFilter() 方法,而没有执行 Servlet 中的 doGet() 方法。如果想让被 Filter 拦截的请求能传递给最初所请求的 Servlet,就需要在 Filter 的 doFilter() 方法里加上 chain.doFilter(); 这句代码,表示本次拦截结束、释放本次的请求及响应,代码如下:


public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException{
        System.out.println("过滤器01的执行方法:doFilter()方法…");
        chain.doFilter(request, response);
    }

修改过滤器 MyFirstFilter.java 后重启服务,再次运行并点击超链接,可在控制台看到如下结果:

图片描述

从输出结果可以得知 index.jsp 发出的请求确实先被过滤器进行了拦截处理,然后又执行了 Servlet 中的 doGet() 方法。

之前讲过,Filter 能对请求和响应都进行拦截。在 Filter 中,在 chain.doFilter() 之前的代码用于拦截处理请求,chain.doFilter() 之后的代码用于拦截处理响应,现将过滤器修改如下。

public class MyFirstFilter implements Filter{
    …
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException{
        System.out.println("拦截请求01…");
        chain.doFilter(request, response);
        System.out.println("拦截响应01…");
    }
}

再次重启服务,点击超链接,可在控制台看到如下结果。

图片描述