JavaWeb 中 Filter过滤器

news2024/9/28 1:15:04

Filter过滤器

在这里插入图片描述

每博一文案

师傅说:人生无坦途,累是必须的背负,看多了,人情人暖,走遍了离合聚散,有时会
在心里对自己说,我想,我是真的累了,小时候有读不完的书,长大后有赚不尽的力。
白天在外要奋斗打拼,把心事都藏起来,笑脸相迎,做一个合格的员工,晚上回家要照顾家人。
把家务都打理的井井有条,做一个称职的伴侣,习惯了所有事情,自己扛,习惯了所有委屈自己消化,
有时候莫名的低落,什么话都不想说,只想一个静静的发呆,有时会突然的烦躁,什么事都不想做,
只想让自己好好的放松,偶尔也会向往过一份属于自己的生活。
没有那么多责任,要背负只做自己想做的事,累了就停下类休息吧,烦了就给自己放个假吧。
这个世上没有铁打的身体,该休息时就得休息。
这个世上没有坚强的心灵,该哭泣时就该哭泣。
看看碧海蓝天,听听轻歌曼舞,会会知己老友,品品清茶,美酒,生活本就可以多姿多彩。
人生说到底,活的是心气,为累过,方知闲,为苦过,方知甜,随缘自在,勿忘心安,便是活着的最美状态。
                                       ——————《一禅心灵庙语1》

文章目录

  • Filter过滤器
    • 每博一文案
    • 1. Filter 过滤器的概述
    • 2. Filter 过滤器的编写
    • 3. Filter 过滤器的执行过程解析
      • 3.1 Filter 过滤结合 Servlet 的使用
    • 4. Filter 过滤器的拦截路径:
      • 4.1 精确匹配路径
      • 4.2 目录匹配
      • 4.3 前后缀名路径匹配
      • 4.4 所有路径匹配
    • 5. 设置 Filter 执行顺序
    • 6. Filter 过滤器中的责任链设计模式思想
    • 7. 运用 Filter 过滤器的方式优化 oa 项目的一个登录验证:
    • 8. 总结:
    • 9. 最后:

1. Filter 过滤器的概述

在这里插入图片描述

在一个比较复杂的Web应用程序中,通常都有很多URL映射,对应的,也会有多个Servlet来处理URL。

我们考察这样一个论坛应用程序:

            ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
               /             ┌──────────────┐
            │ ┌─────────────>│ IndexServlet │ │
              │              └──────────────┘
            │ │/signin       ┌──────────────┐ │
              ├─────────────>│SignInServlet │
            │ │              └──────────────┘ │
              │/signout      ┌──────────────┐
┌───────┐   │ ├─────────────>│SignOutServlet│ │
│Browser├─────┤              └──────────────┘
└───────┘   │ │/user/profile ┌──────────────┐ │
              ├─────────────>│ProfileServlet│
            │ │              └──────────────┘ │
              │/user/post    ┌──────────────┐
            │ ├─────────────>│ PostServlet  │ │
              │              └──────────────┘
            │ │/user/reply   ┌──────────────┐ │
              └─────────────>│ ReplyServlet │
            │                └──────────────┘ │
             ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─

各个Servlet设计功能如下:

  • IndexServlet:浏览帖子;
  • SignInServlet:登录;
  • SignOutServlet:登出;
  • ProfileServlet:修改用户资料;
  • PostServlet:发帖;
  • ReplyServlet:回复。

其中,ProfileServlet、PostServlet和ReplyServlet都需要用户登录后才能操作,否则,应当直接跳转到登录页面。

我们可以直接把判断登录的逻辑写到这3个Servlet中,但是,同样的逻辑重复3次没有必要,并且,如果后续继续加Servlet并且也需要验证登录时,还需要继续重复这个检查逻辑。

为了把一些公用逻辑从各个Servlet中抽离出来,JavaEE的Servlet规范还提供了一种Filter组件,即过滤器,它的作用是,在HTTP请求到达Servlet之前,可以被一个或多个Filter预处理,类似打印日志、登录检查等逻辑,完全可以放到Filter中。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

什么是 Filter 过滤器:

  1. Filter 过滤器它是 JavaWeb 的三大组件之一。
    三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器
  2. Filter 过滤器它是 JavaEE 的规范。也就是接口
  3. Filter 过滤器它的作用是:拦截请求,过滤响应。

拦截请求常见的应用场景有:
1.权限检查 2.日记操作 3.事务管理 ……等等

一般情况下,都是在过滤器当中编写公共代码。提高代码的复用性.

2. Filter 过滤器的编写

  • 第一步:编写一个Java类实现一个接口:jarkata.servlet.Filter。并且实现这个接口当中所有的方法。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • init( )方法:在Filter对象第一次被创建之后调用,并且只调用一次。与Servlet中的init()方法类似,filter中的init()方法用于初始化过滤器。开发者可以在init()方法中完成与构造方法类似的初始化功能。如果初始化代码中要用到FilterConfig对象,则这些初始化代码只能在filter的init()方法中编写,而不能在构造方法中编写。 default 是接口中的一个默认方法,基于 jdk8 新特性,默认方法可以不用重写,如果有需要也是可以重写的.
public default void init(FilterConfig filterConfig) throws ServletException {}
  • doFilter( )方法:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。需要注意的是,服务器响应的时候,该方法也是会被执行的。doFilter方法类似于Servlet接口的service()方法。当客户端请求目标资源时,容器会筛选出符合<filter-mapping>标签中<url-pattern>的filter,并按照声明<filter-mapping>的顺序依次调用这些filter的doFilter()方法。doFilter()方法有多个参数,其中参数requestresponse为Web服务器或filter链中的上一个filter传递过来的请求和响应对象。参数chain代表当前filter链的对象,只有当前filter对象中的doFilter()方法内部需要调用FilterChain对象的doFilter方法时,才能把请求交付给filter链中的下一个filter或目标程序处理。这个是抽象方法,必须重写。
 public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;
  • destroy( )方法:在Filter对象被释放/销毁之前调用,并且只调用一次。 filter中的destroy()方法Servlet中的destroy()作用类似,在Web服务器卸载filter对象之前被调用,用于释放被filter对象打开的资源。 default 是接口中的一个默认方法,基于 jdk8 新特性,默认方法可以不用重写,如果有需要也是可以重写的.
 public default void destroy() {}
package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import java.io.IOException;

public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("TestFilter 中的 init() 方法 初始化 执行了");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("TestFilter 中的 doFilter() 方法执行了");
    }

    @Override
    public void destroy() {
        System.out.println("TestFilter 中的 destroy() 方法 销毁 执行了");
    }
}

  • 第二步:在web.xml文件中对 Filter进行配置。这个配置和 Servlet很像。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <filter>
        <!--        两个 name 名是要保持一致的-->
        <filter-name>filter</filter-name>
        <!--        对应的全类路径名,全类限定名-->
        <filter-class>com.RainbowSea.filter.TestFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>filter</filter-name>
        <!--        Filter 的映射路径,以 / 开始-->
        <url-pattern>/a.do</url-pattern>
    </filter-mapping>
    
    
</web-app>

如下是关于: Filter的所有的 web.xml 中的配置属性信息:

标签作用
<filter>指定一个过滤器
<filter-name>用于为过滤器指定一个名称,该元素的内容不能为空
<filter-class>用于指定过滤器的完整的限定类名
<init-param>用于为过滤器指定初始化参数
<param-value><init-param>的子参数,用于指定参数的名称
<filter-mapping>用于设置一个filter所负责拦截的资源
<filter-name><filter-mapping>子元素,用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名称
<url-pattern>用于设置filter所拦截的请求路径
<servlet-name>用于指定过滤器所拦截的Servlet名称

执行效果:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如果Servlet版本大于3.0,也可以使用注解 @WebFilter()的方式来配置filter。

]

如下:

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;


@WebFilter("/a.do")
public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("TestFilter 中的 init() 方法 初始化 执行了");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("TestFilter 中的 doFilter() 方法执行了");
    }

    @Override
    public void destroy() {
        System.out.println("TestFilter 中的 destroy() 方法 销毁 执行了");
    }
}

如下是关于:@WebFilter() 的属性的说明:

属性类型是否必须说明
asyncSupportedboolean指定filter是否支持异步模式
dispatcherTypesDispatcherType[]指定filter对哪种方式的请求进行过滤
filterNameStringfilter名称
initParamsWebInitParam[]配置参数
displayNameStringfilter显示名
servletNamesString[]指定对哪些Servlet进行过滤
urlPatterns/valueString[]两个属性作用相同,指定拦截的路径

web.xml可以配置的filter属性都可以通过@WebServlet的方式进行配置。不过一般不推荐使用注解方式来配置filter,因为如果存在多个过滤器,使用web.xml配置filter可以控制过滤器的执行顺序,如果使用了注解方式,则不可以这样做了。该 Filter 执行顺序该文章的后面会详细说明,所以请大家,耐心阅读。谢谢。

3. Filter 过滤器的执行过程解析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

当用户向服务器发送request请求时,服务器接受该请求,并将请求发送到第一个过滤器中进行处理。如果有多个过滤器,则会依次经过filter2,filter3,…,filter n。接着调用Servlet中的service()方法,调用完毕后,按照与进入时相反的顺序,从过滤器filter n开始,依次经过各个过滤器,直到过滤器filter 1.最终将处理后的结果返回给服务器,服务器再反馈给用户。

filter进行拦截的方式也很简单,在HttpServletRequest到达Servlet之前,filter拦截客户的HttpServletRequest,根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。在HttpServletRequest到达客户端之前,拦截HttpServletRequest,根据需要检查HttpServletRequest,也可以修改HttpServletResponse头和数据。

3.1 Filter 过滤结合 Servlet 的使用

想要让 Filter 可以过滤用户对 Servlet 发送的请求,必须满足如下两个条件:

  • 第一个:在 Filter 过滤器当中的 doFilter() 方法中编写:chain.doFilter(request, response) 方法:该方法的作用是:执行下一个过滤器,如果下面没有过滤器了(Filter 过滤器之间的 映射路径是相同的情况下),执行最终的Servlet(在Servlet 与 Filter 过滤器的映射路径是相同的情况下。)
@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet
        chain.doFilter(request,response);
    } 
  • 第二:用户发送的请求路径是否和Servlet的请求路径一致。而 Filter过滤器的映射路径是否包含/和Servlet的请求路径一致。只有 Filter 过滤器映射路径包含/和 Servlet 的请求映射路径是一致的,Filter才会过滤该用户方法的请求信息。
  • 注意:Filter的优先级,天生的就比Servlet优先级高。:比如:/a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。

举例:

在这里插入图片描述

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;


@WebFilter("/a.do")
public class TestFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("TestFilter 中的 doFilter() 方法 begin 开始执行了");

        // 表示:执行下一个 Filter(同一个 映射的路径,如果有下一个Filter 的话),没有就执行(同一个映射的路径的 Servlet )
        chain.doFilter(request,response);

        System.out.println("TestFilter 中的 doFilter() 方法 end 执行结束");
    }

}

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;


@WebServlet("/a.do")
public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        System.out.println("AServlet 执行了");
    }
}

测试效果:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

案例举例:

LoginFilter 过滤器,获取到客户端发送过来的请求,进行一个过滤,判断用户的账号和密码是否正确,正确的话,LoginFilter 过滤器放行,到 LogServlet 表示登录成功。如果用户的账号和密码是错误的,则让提示用户密码错误,请重新登录。

这里为了方便演示核心,我们就将 账号和密码写死了,账号为: admin ,密码为 123

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;


@WebFilter("/login")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest requ, ServletResponse resp, FilterChain chain) throws IOException,
            ServletException {


        HttpServletRequest request = (HttpServletRequest) requ;
        HttpServletResponse  response= (HttpServletResponse) resp;

        request.setCharacterEncoding("UTF-8");  // 设置获取到的请求信息的字符编码:

        // 获取到用户的请求信息

        String name = request.getParameter("user");
        String password = request.getParameter("password");


        // 过滤器:判断用户登录的账号和密码是否正确
        if ("admin".equals(name) && "123".equals(password)) {
            // 正确:放行
            // 表示:执行下一个 Filter(同一个 映射的路径,如果有下一个Filter 的话),没有就执行(同一个映射的路径的 Servlet )
            chain.doFilter(request,response);

        } else {
            // 跳转至登录失败的页面
            response.sendRedirect(request.getContextPath()+"/error.jsp");
        }
    }
}

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;


@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charSet=utf-8");

        PrintWriter writer = response.getWriter();

        writer.println("登录成功");
    }
}


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h3>登录失败,请重新登录</h3>

</body>
</html>

在这里插入图片描述

在这里插入图片描述

Filter 过滤器的生命周期

在这里插入图片描述

当Web容器启动时,会根据web.xml中声明的filter顺序依次实例化这些filter。然后在Web应用程序加载时调用init()方法,随机客户端有请求时调用doFilter()方法,并且根据实际情况的不同,doFilter()方法可能被调用多次。最后Web应用程序卸载时调用destroy()方法。

Filter 过滤器与 Servlet 的区别:

  • servlet对象默认情况下,在服务器启动的时候是不会新建对象的。
  • Filter对象默认情况下,在服务器启动的时候会新建对象。
  • Servlet是单例的。Filter也是单例的。(单实例,但是它们都是假单例,因为真单例的构造器是 private 的,而这两个类都不是私有的构造器)
  • Filter 和Servlet对象生命周期一致。
  • 唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。
  • Filter的优先级,天生的就比Servlet优先级高。比如:/a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。

4. Filter 过滤器的拦截路径:

关于Filter的配置路径:不同的 Filter 映射路径,所拦截用户请求的也是不一样的。

关于 Filter 过滤器路径的配置:大致可以分别如下四种:

  • 精确匹配路径:/a.do、/b.do、/dept/save。这些配置方式都是精确匹配。

  • 目录匹配:/admin/*

  • 匹配所有路径: /*
  • 前后缀名路径匹配:后缀:*.do 后缀匹配。不要以 / 开始,前缀:/dept/* 前缀匹配。

4.1 精确匹配路径

只有完美匹配,一个符号,一个字符,不能错误的路径。Filter 只会过滤用户访问该:/target/dep路径的信息才会拦截判断是否放行。其他用户访问的路径一概不会进行 Filter 过滤器过滤

<filter-mapping>
    <filter-name>Filter</filter-name>
    <url-pattern>/target/dep</url-pattern>  <!--精确匹配-->
</filter-mapping>

4.2 目录匹配

Filter 会过滤用户访问该:/admin/* admin子路径包含 admin路径的信息:比如:/admin/test/admin/test/test2才会拦截判断是否放行。其他用户访问的路径一概不会进行 Filter 过滤器过滤。

<filter-mapping>
    <filter-name>Filter</filter-name>
    <url-pattern>/admin/*</url-pattern> <!--目录匹配-->
</filter-mapping>

4.3 前后缀名路径匹配

后缀路径匹配:以上配置的路径,表示请求地址必须以.do 结尾才会被 Filter 过滤器拦截判断是否放行。

注意的是:不要以 / 开始 ,不然就失效了。

<filter-mapping>
    <filter-name>Filter</filter-name>
    <url-pattern>*.do</url-pattern> <!--目录匹配-->
</filter-mapping>

前缀路径匹配:以上配置的路径,表示请求地址必须以/admin/ 开头才会被 Filter 过滤器拦截判断是否放行

<filter-mapping>
    <filter-name>Filter</filter-name>
    <url-pattern>/admin/*</url-pattern> <!--目录匹配-->
</filter-mapping>

4.4 所有路径匹配

/* 表示对应任何请求地址,Filter 过滤器都会进行一个拦截判断是否放行,那么这个路径的资源不存在也会,进行一个拦截判断是否放行。这种方式虽然简单,但是,这个代价比较到,效率低,对于特殊的路径请求要放行的你可能需要编写大量的逻辑判断进行一个拦截放行。不建议使用。

<filter-mapping>
    <filter-name>Filter</filter-name>
    <url-pattern>/*</url-pattern> <!--目录匹配-->
</filter-mapping>

5. 设置 Filter 执行顺序

一个 Servelt 是可以设置多个 Filter 过滤器的,当我们设置了多个 Filter 过滤器,其中 Filter 过滤器的执行顺序该如何设置呢?

从上面文章的内容,我们知道了 Filter 的映射路径设置有两种方式:

  1. 注解:@WebFilter()
  2. 配置 web.xml 文件的方式。这种方式 推荐使用

两种设置 Filter的方式不同,对于设置的执行顺序的方式也是不一样的。

对于 Filter 执行顺序的设置,虽然有两种方式,但是推荐使用 web.xml 配置文件的方式,因为使用 注解@WebFilter() 的方式的话,我们需要更加定义的类名的字母顺序的方式来,设置Filter的执行顺序,这样会改变一个类名的见名之意。而如果是使用 web.xml 配置文件的方式,直接更加 Filtet 编写的配置是先后顺序就可以了。

过滤器的调用顺序,遵循栈数据结构。

第一种:注解的方式:设置Filter过滤器的执行顺序:

注解的方式:

执行顺序是:比较 Filter 这个类名。就是你定义的这个类名 implements (实现) Filter 的类名

在这里插入图片描述

  • 比如:FilterA和FilterB,则先执行FilterA
  • 比如:Filter1和Filter2,则先执行Filter1

举例验证:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

通过:配置 web.xml 文件的方式,如何设置 Filter 的执行顺序:

web.xml 中是:依靠 <filter-mapping>标签的配置位置,越靠上优先级越高,就越先执行其中的 Filter的 doFilter() 方法。

举例证实:

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <filter>
        <filter-name>FilterB</filter-name>
        <filter-class>com.RainbowSea.filter.FilterB</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilterB</filter-name>
        <url-pattern>/A</url-pattern>
    </filter-mapping>

    <filter>
        <!--        两个 name 名是要保持一致的-->
        <filter-name>FilterA</filter-name>
        <!--        对应的全类路径名,全类限定名-->
        <filter-class>com.RainbowSea.filter.FilterA</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilterA</filter-name>
        <!--        Filter 的映射路径,以 / 开始-->
        <url-pattern>/A</url-pattern>
    </filter-mapping>




</web-app>
package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;


@WebServlet("/A")
public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException,
            IOException {
        System.out.println("AServlet doGet()  执行了");


    }
}

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;


import java.io.IOException;



public class FilterB implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        System.out.println("FilterB doFilter() 执行了");

        // 表示执行后面的(映射路径相同的Filter 过滤器),如果后面没有的话执行(映射路径相同的 Servlet)
        chain.doFilter(request,response);

    }
}

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;


import java.io.IOException;




public class FilterA implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("FilterA  doFilter() 执行了");

        // 表示执行后面的(映射路径相同的Filter 过滤器),如果后面没有的话执行(映射路径相同的 Servlet)
        chain.doFilter(request,response);
    }
}

在这里插入图片描述

6. Filter 过滤器中的责任链设计模式思想

23种设计模式 : 责任链设计模式

过滤器最大的优点:

在程序编译阶段不会确定调用顺序。因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式。

**责任链设计模式最大的核心思想:在程序运行阶段,动态的组合程序的调用顺序。**在上面对于 Filter的使用当中,我们已经体验到了,Filter 的动态调用其中的 doFilter() 方法,通过修改其中的 web.xml 对 Filter 的配置顺序。

下面我们演示一下不是 :责任链设计模式的方式:

如下这种方式:是我们写死了的,想要改变其中的执行顺序,就必须通过修改其中的源码当中的,代码的执行顺序,无法通过通过配置文件的方式,修改调用的顺序。

package com.RainbowSea.filter;

public class Test {
    public static void main(String[] args) {
        m1();
    }

    private static void m1() {
        System.out.println("m1() begin ");
        m2();
        System.out.println("m1() end ");
    }

    private static void m2() {
        System.out.println("m2() begin ");
        m3();
        System.out.println("m2() end ");
    }

    private static void m3() {
        System.out.println("m3() begin ");

        System.out.println("m3() end ");
    }
}

在这里插入图片描述

7. 运用 Filter 过滤器的方式优化 oa 项目的一个登录验证:

关于 oa 项目的,大家可以移步至🔜🔜🔜 B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能_ChinaRainbowSea的博客-CSDN博客 先了解,有助于后面的阅读。

思路:

有什么情况下不能拦截: 要过滤通过:

目前编写的路径是/* 表示所有请求均拦截:

用户访问 index.jsp 的时候不能拦截, 要放行:
用户登录过了,这个时候,需要放行。
用户要去登录,这个也是不能拦截,要放行:让用户登录。
其他情况:比如,没有登录过,就想访问资源的话,过滤拦截,
登录失败,过滤拦截
用户退出,不能拦截,要让其过去

需要注意的是:这里我们 Filter 过滤器当中的 doFilter() 方法中的 request 是来自: ServletRequest 接口的,当时如下使用的一些方法 sendRedirect() 是来自于: HttpServletRequest 这个接口的,所以我们需要强制类型转换一下。 HttpServletRequest 是 extends 继承了 ServletRequest 接口的。

在这里插入图片描述

在这里插入图片描述

package com.RainbowSea.Filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

@WebFilter("/*")
public class LoginCheckFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        // 需要强制类型转换以下,因为 下面的一些getServletPath 方法是来自 HttpServletRequest的
        //不是 来自 ServletRequest 的
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        String servletPath = request.getServletPath();  // 获取到浏览器当中的uri

        // 获取session 这个 session 是不不需要新建的
        // 只是获取当前session ,获取不到这返回null,
        HttpSession session = request.getSession(false);  // 获取到服务器当中的session ,没有不会创建的



        // 过滤器:
        /*
        有什么情况下不能拦截: 要过滤通过:
          1. 目前编写的路径是/* 表示所有请求均拦截:

          用户访问 index.jsp 的时候不能拦截, 要放行:
          用户登录过了,这个时候,需要放行。
          用户要去登录,这个也是不能拦截,要放行:让用户登录。
          其他情况:比如,没有登录过,就想访问资源的话,过滤拦截,
          登录失败,过滤拦截
          用户退出,不能拦截,要让其过去


           if ("/dept/list".equals(servletPath)) {
                doList(request, response);
            } else if ("/dept/detail".equals(servletPath)) {
                doDetail(request, response);
            } else if ("/dept/delete".equals(servletPath)) {
                doElete(request,response);
            } else if("/dept/save".equals(servletPath)) {
                doSave(request,response);
            } else if("/dept/modify".equals(servletPath)) {
                doModify(request,response);


         */

        if("/index.jsp".equals(servletPath) || (("/welcome").equals(servletPath)) ||
                ( session != null && session.getAttribute("username") != null)
        || "/user/login".equals(servletPath) || "/user/exit".equals(servletPath)) {
            // 双重的判断,一个是 session 会话域要存在,其次是 会话域当中存储了名为 "username" 的信息

            chain.doFilter(request,response);  // 放行,让其向下一个过滤器,或者是Servlet 执行
        } else {
            response.sendRedirect(request.getContextPath() + "/index.jsp");  // 访问的web 站点的根即可,自动找到的是名为 index.jsp
            // 的欢迎页面(注意这里被优化修改了:局部优先)注意:这里修改了,需要指明index.jsp登录页面了,因为局部优先
        }
    }
}

8. 总结:

  1. Filter 过滤器它是 JavaEE 的规范。也就是接口Filter 过滤器它的作用是:拦截请求,过滤响应。
  2. Filter 过滤器的常用的三个方法:
  • init方法:在Filter对象第一次被创建之后调用,并且只调用一次。
  • doFilter方法:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。
  • destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。
  1. 目标Servlet是否执行,取决于两个条件:
  • 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。
  • 第二:用户发送的请求路径是否和Servlet的请求路径一致。
  1. chain.doFilter(request, response); 这行代码的作用:执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet。

  2. 注意:Filter的优先级,天生的就比Servlet优先级高。

  • /a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。
  1. Filter 的执行原理图:
    在这里插入图片描述

  2. Filter 过滤器拦截路径:

    • /a.do、/b.do、/dept/save。这些配置方式都是精确匹配。
    • /* 匹配所有路径。
    • *.do 后缀匹配。不要以 / 开始
    • /dept/* 前缀匹配
  3. Filter 过滤器的设置执行顺序:两种方式:

  • 在web.xml文件中进行配置的时候,依靠filter-mapping标签的配置位置,越靠上优先级越高。

  • @WebFilter的时候,执行顺序是:比较Filter这个类名。

    • 比如:FilterA和FilterB,则先执行FilterA。
    • 比如:Filter1和Filter2,则先执行Filter1
  1. Filter 过滤器中的责任链设计模式思想:责任链设计模式最大的核心思想:在程序运行阶段,动态的组合程序的调用顺序
  2. 编写 Filter 过滤器的技巧:先想明白一个就是:哪些资源/功能方法是需要验证才能放行的,哪些是不需要验证直接就放行通过的。想明白这两点,基本上编写的 Filter 过滤器没有任何问题了。

9. 最后:

⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ 感谢如下博主的分享: ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

  • https://www.liaoxuefeng.com/wiki/1252599548343744/1266264823560128
  • https://blog.csdn.net/weixin_45007073/article/details/120071622?ops_request
  • https://blog.csdn.net/weixin_45024585/article/details/112749271?ops_request_

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/504423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大学生志愿者管理信息系统设计与实现(论文+源码)_kaic

摘 要 在国家的十四五期间&#xff0c;志愿服务成为推动社会文明发展的重要力量。大学生是志愿活动的中坚力量。现有的志愿管理工作不能满足志愿活动的需要&#xff0c;存在活动找不到志愿者&#xff0c;志愿者找不到活动的情况。为服务良好的志愿服务体系&#xff0c;对大学…

Hologres技术揭秘: JSON半结构化数据的极致分析性能

作者&#xff1a;王华峰&#xff08;花名继儒&#xff09;&#xff0c;Hologres研发 近年来&#xff0c;随着移动端应用的普及&#xff0c;应用埋点、用户标签计算等场景开始诞生&#xff0c;为了更好的支撑这类场景&#xff0c;越来越多的大数据系统开始使用半结构化JSON格式…

保姆级教程:OpenAI获取Account详细教程2023/05/09最新

前言 最近OpenAI封的比较严重&#xff0c;建议大家不要批量&#xff0c;容易被封&#xff0c;也不要用公用的IP 一、上网 首先你要学会上网&#xff08;懂&#xff1f;&#xff09;、这一步是必须的。 现在MG等地方的IP已经很难了&#xff0c;可以选择一些小众国家的IP 二、开…

【论文阅读】Online multi-sensor calibration based on moving object tracking

目录 Online multi-sensor calibration based on moving object trackingAbstract1. Introduction2. Proposed Method2.1 Object Detection2.2 Tracking of moving objects2.3 Track-to-track association2.4 Decalibration(解关联) detection2.5 Graph-based extrinsic calibr…

花菁染料CY5.5标记活性脂 Cy5.5-NHS

Cy5.5 NHS ester用于染色蛋白质、标记DNA、标记细胞表面抗原、标记抗体和其他生物分子。Cy5.5 NHS ester还可以用于分子影像学&#xff0c;可以追踪细胞内的变化。它还可以用于荧光免疫检测&#xff0c;以检测细胞表面抗原和抗体。 产品名称&#xff1a;五甲川花菁染料CY5.5标记…

OpenGL 4.0的Tessellation Shader(细分曲面着色器)

细分曲面着色器&#xff08;Tessellation Shader&#xff09;处于顶点着色器阶段的下一个阶段&#xff0c;我们可以看以下链接的OpenGL渲染流水线的图&#xff1a;Rendering Pipeline Overview。它是由ATI在2001年率先设计出来的。 目录 细分曲面着色器细分曲面Patch细分曲面控…

手把手实现项目中自定义动态数据源?

手把手实现项目中自定义动态数据源&#xff1f; 第一步&#xff1a;创建项目&#xff0c;添加POM依赖第二步&#xff1a;添加application.yml配置文件第三步&#xff1a;自定义注解指定使用哪个数据源第四步&#xff1a;创建DynamicDataSourceContextHolder工具类&#xff0c;存…

LeetCode:347. 前 K 个高频元素

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 题解目录 一、&#x1f331;[347. 前 K 个高频元素](https://leetcode.cn/problems/bina…

安全防线再升级 | 中睿天下全流量安全分析系统重磅回归

随着信息化的加速&#xff0c;企业网络日趋完善。企业数字化的加速&#xff0c;让越来越多的关键业务运行在计算机网络基础之上&#xff0c;越来越多的重要信息通过网络传送&#xff0c;企业网络面临日益严重的安全威胁&#xff0c;这些安全威胁以窃取信息和收集情报为主&#…

基于docker安装MySQL

为了更好的管理&#xff0c;打算把MySQL、redis等服务放在虚拟机中统一部署&#xff0c;这样不会因为这些服务的问题影响到系统本身。前段时间正好在看docker相关的内容&#xff0c;打算在虚拟机中通过docker来使用MySQL等服务。 这次先记录安装MySQL的过程。 docker安装 首先…

超越 Nginx!号称下一代 Web 服务器,用起来够优雅!

Nginx是一款非常流行的Web服务器&#xff0c;在Github上已有16KStar&#xff0c;我们经常用它来做静态资源托管或反向代理。最近发现了一款全新的Web服务器Caddy&#xff0c;Star数超越Nginx&#xff0c;标星38KStar。试用了一下Caddy&#xff0c;发现它使用起来比Nginx优雅多了…

掌握这五个核心步骤,让你的方案完美无缺

写策划方案怎么来写&#xff0c;可能会是刚入行策划人的难点&#xff0c;策划方案其实就是一份营销计划&#xff0c;一份完整的策划能让策划人看清自己、认清竞争对手&#xff0c;形成对市场的整体认知。 一般大的营销策划方案里面会用到STP、SWOT和4P等模型&#xff0c;模型本…

【Java多线程编程】创建线程的基本方式

大家好&#xff0c;我是一只爱打拳的程序猿。今天给大家带来的内容是 Java 创建线程的基本方式。 多线程的基本创建方式: 继承Thread类实现Runnable接口匿名内部类使用lambda表达式 目录 1. 继承Thread类 1.1 Thread类启动线程 2. 实现Runnable接口 2.1 创建Thread类实例…

while和until的使用方法(还有一些小实验:计算器、猜价格游戏、购物)

while和until的使用方法 一、while用法二、Until循环语句三、猜价格小实验四、计算器实验六、购物实验 一、while用法 for循环语句非常适用于列表对象无规律&#xff0c;且列表来源以固定&#xff08;如某个列表文件&#xff09;的场合。而对于要求控制循环次数&#xff0c;操…

C++学习day--06 向计算机输入数据

1、输入 当缓冲区为空时&#xff0c;程序才会暂停&#xff0c;让用户输入数据。 输入回车后&#xff0c;数据全部送到输入缓冲区。 #include <iostream> #include <Windows.h> int main( void ){ char girlType; int salary; float height; std::cout &l…

记录--极致舒适的Vue页面保活方案

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 为了让页面保活更加稳定&#xff0c;你们是怎么做的&#xff1f; 我用一行配置实现了 Vue页面保活是指在用户离开当前页面后&#xff0c;可以在返回时恢复上一次浏览页面的状态。这种技术可以让用户享…

shell脚本4函数

文章目录 shell脚本函数1 函数概述2 定义2.1 形式2.2 使用原则2.3 函数传参2.4 函数变量的作用范围 3 递归3.1 阶乘 4 函数库5 实验5.1 阶乘5.2 递归目录5.3 调用函数库 shell脚本函数 1 函数概述 1、将命令序列按格式写在一起 2、可方便重复使用命令序列 使用函数可以避免代码…

FS4067升压充电8.4V锂电池充电IC电流3A

FS4067升压型5V升压充电8.4V两串锂电池充电IC&#xff0c;工作电压范围于 2.7V 到 6.5V 的 PFM 升压型两节锂电池充电控制集成电路。 FS4067采用恒流和恒压模式对电池进行充电管理&#xff0c;内部集成有基准电压源&#xff0c; 电感电流检测单元&#xff0c;电池电压检测电路和…

【原创】强烈推荐三个可视化模块,绘制的图表真的很酷炫!!

Matplotlib是Python编程语言中最受欢迎的绘图库之一。它提供了一套面向对象的API&#xff0c;可将图表嵌入到使用通用GUI工具包&#xff08;如Tkinter、wxPython、Qt或GTK&#xff09;的应用程序中。Matplotlib还常用于创建静态、动画和交互式的Python数据可视化。它能够绘制各…

【Unity】在Unity下使用websocket的一些经验

首先&#xff0c;先上大家都知道的简介&#xff0c;这一版是我认为比较清晰的。。。虽然在度娘的教导和知乎的教导下&#xff0c;总算认识了websocket&#xff0c;但这个过程比较艰辛&#xff0c;给大家发出来看一下&#xff1a; --------------------------------------------…