Filter过滤器

发布时间 2023-12-14 13:19:54作者: guoyu1

一 认识过滤器

1.1 什么是过滤器

Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

1.2 过滤器如何实现功能

1在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。

2在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

3 Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,Web服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,doFilter方法中有一个filterChain对象,用于继续传递给下一个filter,在传递之前我们可以定义过滤请求的功能,在传递之后,我们可以定义过滤响应的功能

1.3 过滤器如何使用

采用三步走策略使用filter

1开发后台资源 静态资源(html,css … …)或者动态资源(Servlet,Jsp)

2开发Filter

3在web.xml中配置Filter拦截哪些资源

开发Servlet

public class MyController1 extends HttpServlet {
 @Override
 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Mycontroller1的服务方法");
    }
}

开发Filter

自定义类,实现javax.servlet.Filter;接口,重写init,doFilter,destory方法

public class MyFilter1 implements Filter {
 /**
     * 初始化方法
 * @param filterConfig
 * @throws ServletException
     */
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
 /**
     * 执行过滤的方法
 * @param servletRequest
 * @param servletResponse
 * @param filterChain
 * @throws IOException
     * @throws ServletException
     */
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 // 过滤请求
 System.out.println("MyFiter1在请求到达servlet之前的代码处理");
 // 传递过滤器
 filterChain.doFilter(servletRequest,servletResponse);
 // 过滤响应
 System.out.println("myFilter1在响应回到浏览器之前的代码处理");
    }
 
 /**
     * 销毁方法
 */
 @Override
 public void destroy() {
 
    }
}

配置Filter和Servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置servlet-->
    <servlet>
        <servlet-name>mycontroller1</servlet-name>
        <servlet-class>com.bjsxt.controller.MyController1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>mycontroller1</servlet-name>
        <url-pattern>/mycontroller1</url-pattern>
    </servlet-mapping>
    <!--配置filter-->
    <filter>
        <filter-name>myfilter1</filter-name>
        <filter-class>com.bjsxt.filter.MyFilter1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myfilter1</filter-name>
        <!--通过servlet那么确定拦截的资源-->
       <!-- <servlet-name>mycontroller1</servlet-name>-->
        <!--通过请求的映射路径匹配拦截的资源-->
        <url-pattern>/mycontroller1</url-pattern>
    </filter-mapping>
</web-app>

总结:

1在doFilter方法中,我们可以通过filterChain.doFilter方法控制请求是否继续向后传递

2在doFilter方法中,我们同样可以使用HttpRequest处理请求,使用HttpResponse对象作出响应

1.4 过滤器的生命周期

同servlet对象一样,Filter对象的创建也是交给web服务器完成的,在web服务器创建和使用及最后销毁filter时,会调用filter对应的方法

初始化方法:

public void init(FilterConfig filterConfig);

和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

拦截请求方法

public void doFilter

这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。

销毁方法:

public void destroy();

Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

测试代码

public class MyFilter1 implements Filter {
 /**
     * 初始化方法
 * @param filterConfig
 * @throws ServletException
     */
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter1初始化方法");
    }
 
 /**
     * 执行过滤的方法
 * @param servletRequest
 * @param servletResponse
 * @param filterChain
 * @throws IOException
     * @throws ServletException
     */
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 // 过滤请求
 System.out.println("MyFiter1在请求到达servlet之前的代码处理");
 // 传递过滤器
 filterChain.doFilter(servletRequest,servletResponse);
 // 过滤响应
 System.out.println("myFilter1在响应回到浏览器之前的代码处理");
    }
 
 /**
     * 销毁方法
 */
 @Override
 public void destroy() {
        System.out.println("MyFilter1销毁方法");
    }
}

 

[2020-05-14 07:39:44,981] Artifact myfilter1:war exploded: Artifact is being deployed, please wait...
MyFilter1初始化方法
[2020-05-14 07:39:45,614] Artifact myfilter1:war exploded: Artifact is deployed successfully
[2020-05-14 07:39:45,615] Artifact myfilter1:war exploded: Deploy took 634 milliseconds
MyFiter1在请求到达servlet之前的代码处理
Mycontroller1的服务方法
myFilter1在响应回到浏览器之前的代码处理
D:\program4it\tomcat8\apache-tomcat-8.5.27\bin\catalina.bat stop
Using CATALINA_BASE:   "C:\Users\Mark70\.IntelliJIdea2019.2\system\tomcat\Tomcat_8_5_27_FilterAndListener"
Using CATALINA_HOME:   "D:\program4it\tomcat8\apache-tomcat-8.5.27"
Using CATALINA_TMPDIR: "D:\program4it\tomcat8\apache-tomcat-8.5.27\temp"
Using JRE_HOME:        "C:\Program Files\Java\jdk1.8.0_161"
Using CLASSPATH:       "D:\program4it\tomcat8\apache-tomcat-8.5.27\bin\bootstrap.jar;D:\program4it\tomcat8\apache-tomcat-8.5.27\bin\tomcat-juli.jar"
14-May-2020 19:39:58.649 信息 [main] org.apache.catalina.core.StandardServer.await A valid shutdown command was received via the shutdown port. Stopping the Server instance.
14-May-2020 19:39:58.650 信息 [main] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["http-nio-8080"]
14-May-2020 19:39:59.245 信息 [main] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["ajp-nio-8009"]
14-May-2020 19:39:59.830 信息 [main] org.apache.catalina.core.StandardService.stopInternal Stopping service [Catalina]
MyFilter1销毁方法
14-May-2020 19:39:59.844 信息 [main] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["http-nio-8080"]
14-May-2020 19:39:59.845 信息 [main] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["ajp-nio-8009"]
14-May-2020 19:39:59.846 信息 [main] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler ["http-nio-8080"]
14-May-2020 19:39:59.847 信息 [main] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler ["ajp-nio-8009"]
Disconnected from server

1.5 过滤器链的使用

在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。

web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

使用过滤器链的好处是我们可以将不同的过滤功能分散到多个过滤器中,分工明确,避免一个过滤器做太多的业务处理,降低了代码的耦合度,这体现了单一职责的设计原则,应用了责任链的代码设计模式.

决定过滤器的执行顺序是由fileter-mapping标签决定

测试代码

开发第二个Filter

public class MyFilter2 implements Filter {
 /**
     * 初始化方法
 * @param filterConfig
 * @throws ServletException
     */
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter2初始化方法");
    }
 
 /**
     * 执行过滤的方法
 * @param servletRequest
 * @param servletResponse
 * @param filterChain
 * @throws IOException
     * @throws ServletException
     */
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 // 过滤请求
 System.out.println("MyFiter2在请求到达servlet之前的代码处理");
 // 传递过滤器
 filterChain.doFilter(servletRequest,servletResponse);
 // 过滤响应
 System.out.println("myFilter2在响应回到浏览器之前的代码处理");
    }
 
 /**
     * 销毁方法
 */
 @Override
 public void destroy() {
        System.out.println("MyFilter2销毁方法");
    }
}

配置第二个Filter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置servlet-->
    <servlet>
        <servlet-name>mycontroller1</servlet-name>
        <servlet-class>com.bjsxt.controller.MyController1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>mycontroller1</servlet-name>
        <url-pattern>/mycontroller1</url-pattern>
    </servlet-mapping>
    <!--配置filter-->
    <filter>
        <filter-name>myfilter1</filter-name>
        <filter-class>com.bjsxt.filter.MyFilter1</filter-class>
    </filter>
    <filter>
        <filter-name>myfilter2</filter-name>
        <filter-class>com.bjsxt.filter.MyFilter2</filter-class>
    </filter>
    <!--配置过滤器的拦截映射 这里的配置顺序决定了过滤器的过滤顺序-->
    <filter-mapping>
        <filter-name>myfilter1</filter-name>
        <!--通过servlet那么确定拦截的资源-->
       <!-- <servlet-name>mycontroller1</servlet-name>-->
        <!--通过请求的映射路径匹配拦截的资源-->
        <url-pattern>/mycontroller1</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>myfilter2</filter-name>
        <url-pattern>/mycontroller1</url-pattern>
    </filter-mapping>
</web-app>

1.6 过滤器的初始化参数

同servlet一样,filter也可以通过web.xml进行初始化配置,在初始化是,将参数封装进入FilterConfig并在调用init方法时作为实参传入,我们可以在init方法中获取参数.FilterConfig接口为我们提供了如下功能

String getFilterName();//得到filter的名称。 
String getInitParameter(String name);//返回定名称的初始化参数的值。如果不存在返回null. 
Enumeration getInitParameterNames();//返回过滤器的所有初始化参数的名字的枚举集合。 
public ServletContext getServletContext();//返回Servlet上下文对象的引用

测试代码

在web.xml中配置Filter的初始化参数

<filter>
    <filter-name>myfilter2</filter-name>
    <filter-class>com.bjsxt.filter.MyFilter2</filter-class>
    <init-param>
        <param-name>computer</param-name>
        <param-value>华硕</param-value>
    </init-param>
    <init-param>
        <param-name>RAM</param-name>
        <param-value>金士顿</param-value>
    </init-param>
</filter>

在Filter中获取初始化参数

public class MyFilter2 implements Filter {
 /**
     * 初始化方法
 * @param filterConfig
 * @throws ServletException
     */
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter2初始化方法");
        String computer = filterConfig.getInitParameter("computer");
        String ram = filterConfig.getInitParameter("RAM");
        System.out.println("computer:"+computer);
        System.out.println("RAM:"+ram);
    }

启动项目测试

Connected to server
[2020-05-14 08:03:02,056] Artifact myfilter1:war exploded: Artifact is being deployed, please wait...
14-May-2020 20:03:02.267 警告 [RMI TCP Connection(2)-127.0.0.1] org.apache.tomcat.util.descriptor.web.WebXml.setVersion Unknown version string [4.0]. Default version will be used.
MyFilter2初始化方法
computer:华硕
RAM:金士顿

二 过滤器案例开发

1.2 过滤器案例开发之解决post乱码问题

filter可以帮助我们在请求到达servlet之前处理好post乱码问题,这样我们servlet接受的post请求参数是,直接获取参数即可开发form表单

<form action="servlet1" method="post">
  <input type="text" name="username">
  <input type="submit">
</form>

开发后台servlet

@WebServlet("/servlet1")
public class Servlet1  extends HttpServlet{
 @Override
 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        System.out.println("username:"+username);
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("你好");
    }
}

 

开发编码过滤器

public class EncodingFilter implements Filter {
    private String encoding;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //从配置文件中读取编码
        encoding = filterConfig.getInitParameter("charset");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 设置请求编码
        servletRequest.setCharacterEncoding(encoding);
        // 设置响应编码
        servletResponse.setCharacterEncoding(encoding);
        // 传递处理请求
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {

    }
}

配置servlet和Filter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
           version="3.0">

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>com.bjsxt.filter.EncodingFilter</filter-class>
        <init-param>
            <param-name>charset</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/servlet1</url-pattern>
    </filter-mapping>
</web-app>

访问测试:

提交数据

后台接收

返回数据

1.3 过滤器案例开发之登录验证

需求

校验是否登录,如果没有登录执行登录,如果已经登录,那么校验资源

Index 页登录代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <form action="loginCheck" method="post">
      <input type="text" name="username"><br/>
      <input type="password" name="pwd"><br/>
      <input type="submit"><br/>
    </form>
    ${msg}
    <c:remove var="msg" scope="session"></c:remove>
  </body>
</html>

后台servlet代码

@WebServlet("/loginCheck")
public class LoginCheck extends HttpServlet {
 @Override
 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        HttpSession session = req.getSession();
        String pwd = req.getParameter("pwd");
 if("尚学堂".equals(username)&&"1234".equals(pwd)){
            User user= new User("sxt","1234","1");
            session.setAttribute("user",user);
            resp.sendRedirect("b.jsp");
        }else if("北京尚学堂".equals(username)&&"4321".equals(pwd)){
            User user= new User("bjsxt","4321","2");
            session.setAttribute("user",user);
            resp.sendRedirect("a.jsp");
        }else{
            session.setAttribute("msg","登录失败");
            resp.sendRedirect("index.jsp");
        }
 
    }
}

过滤器代码

@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        /*如果请求的是index.jsp 放行*/
        HttpServletRequest request =(HttpServletRequest)servletRequest;
        HttpServletResponse response =(HttpServletResponse)servletResponse;
        String uri = request.getRequestURI();
        //放行 对于index.jsp 的访问  /loginCheck
        if(uri.contains("index.jsp")||uri.contains("loginCheck")){
            filterChain.doFilter(request,response);
            return;
        }

        /*如果已经登录 完成页面跳转*/
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        if(null != user){ 
            filterChain.doFilter(request,response);
        }else{
            response.sendRedirect("login.jsp");
            return;
 }
    }

    @Override
    public void destroy() {

    }
}

配置filter