Web开发之Filter过滤器

Filter 过滤器概述

Filter 过滤器属于 Servlet 规范,从 2.3 版本就开始有了。主要用于对到资源的请求或来自资源的响应执行过滤、筛选操作

当存在过滤器的时候,对于来自客户端的请求来说,请求必须先经过滤器,放行之后,才能到达 Web 资源;对于返回的响应来说,响应同样会经过滤器,才能到达 Web 服务器,进而响应给客户端

Filter 过滤器可以做很多事情,常见的包括

  • 过滤脏敏感字符(绿一些敏感的字符串)
  • 避免中文乱码(统一设置请求和响应的编码)
  • 权限验证(规定只有带指定 SessionCookie 的请求,才能访问资源)
  • 用于实现自动登录

Filter 接口

Filter 接口中有三个抽象方法,其中 init()destroy() 方法作为过滤器的申请周期方法

public interface Filter {
    
    

    public void init(FilterConfig filterConfig) throws ServletException;

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public void destroy();
}

Filter 过滤器的生命周期

  • 诞生:过滤器的实例是在 web 应用被加载时就完成的实例化,并调用 init() 方法初始化的。servlet 容器在实例化 Filter 后只调用 init() 方法一次。在要求过滤器执行任何过滤工作之前,init() 方法必须成功完成
  • 每个过滤器在 init() 初始化方法都会传递一个 FilterConfig 对象,从该对象可以获取其初始化参数。并可通过 FilterConfig 获取 ServletContext 对象,比如可以使用该对象加载筛选任务所需的资源
  • 存活:和应用的生命周期一致的。在内存中是单例的。针对拦截范围内的资源,每次访问都会调用 doFIlter(request,response.chain) 进行拦截
  • 死亡:应用被卸载时,Filter 将被调用,此时会调用 destroy() 方法,该方法只会被调用一次

doFilter 过滤方法

过滤器在 doFilter() 方法中执行过滤操作。doFilter() 方法中有一个 FilterChain 参数对象,该对象由 Servlet 容器创建并传递给开发人员的。FilterChain 表示一个过滤器链,客户端请求的资源在链的末尾

在当前过滤器中,如果有复合过滤规则,那么可以使用 FilterChaindoFilter() 方法调用链中的下一个过滤器器,或者如果调用过滤器是链中的最后一个过滤器,则该方法将调用链末尾的资源

也就是说,一个 Web 应用中可以有多个过滤器,它们将会按照一定顺序形成一个过滤器链,在链的最末尾就是要访问的资源,当一个请求到来的时候,他必须通过所有的过滤器,才能访问到真正的资源

Filter 过滤器的使用

开发一个过滤器很简单,只需要实现 Filter 接口,实现 doFilter() 方法

public class FirstFilter implements Filter {
    
    

    @Override
    public void init(FilterConfig filterConfig) {
    
    
        System.out.println("init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
        System.out.println("放行前");
        HttpServletRequest httpServletRequest= (HttpServletRequest) request;
        // 获取当前请求的URL
        System.out.println(httpServletRequest.getRequestURL());
        // 放行,调用下一个过滤器或者访问资源
        chain.doFilter(request, response);
        System.out.println("放行后");
    }

    @Override
    public void destroy() {
    
    
        System.out.println("destroy");
    }
}

随后我们还需要部署这个 filter。通常情况下,Filter 过滤器在 Web 应用程序的部署描述符中配置,也就是 web.xml 文件,这类似于 Servlet

<filter>
    <filter-name>FirstFilter</filter-name>
    <filter-class>com.example.filter.FirstFilter</filter-class>
    <!--当前过滤器的初始化参数,可以通过在init方法的参数FilterConfig对象获取-->
    <init-param>
        <param-name>aaa</param-name>
        <param-value>bbb</param-value>
    </init-param>
    <init-param>
        <param-name>ccc</param-name>
        <param-value>ddd</param-value>
    </init-param>
</filter>

随后我们还需要定义这个过滤器可以作用于哪些资源或者哪些 Servlet

<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <!--指定拦截指定路径的资源URL-->
    <url-pattern>/aa/*</url-pattern>
</filter-mapping>

<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <!--指定拦截指定的ServletName-->
    <servlet-name>Servlet2</servlet-name>
</filter-mapping>

通过多个 <filter-mapping/> 标签可以为一个过滤器配置多个过滤映射,当然也可以将多个映射一个到一个 <filter-mapping/> 标签中

  • <url-pattern/> 指定过滤器所拦截的资源路径 URL, / 表示所有的 Web 资源都需要途径该过滤器,xxx 表示拦截访问 xxx 后缀的资源的请求

Servlet 3.0 以及之后,支持在 Filter 实现类上直接使用 @WebFilter 注解的方式配置过滤器,降低了配置文件的复杂度

@WebFilter(urlPatterns = "/aa/*",servletNames = "Servlet2")

Filter 过滤器的执行顺序

过滤器链的完整流程顺序是这样的:客户端发送 http 请求到 Web 服务器上,Web 服务器对该请求 URL 找到对应负责的过滤器形成过滤器链,接着从第一个过滤器开始进行过滤操作,也就是调用 Filter.doFilter() 方法,这个方法的逻辑是开发者编写的,当当前请求满足当前过滤器的要求或者是过滤操作完毕之后,应再调用 chain.doFilter() 方法进行放行,该方法又会调用链中的下一个过滤器的 doFilter() 方法继续过滤,如果当前过滤器是过滤器链的最后一个过滤器,那么 chain.doFilter() 方法将会执行资源访问操作,访问完毕之后,将会依照最开始过滤器的调用顺序倒序的返回,接着执行 chain.doFilter() 方法后面的代码。最终将响应结果交给 Web 服务器,Web 服务器再将响应返回给客户端

多个过滤器

当某个请求对应存在多个过滤器时,过滤器之间的执行顺序是通过在 web.xml 文件中某个 Filter 的首个定义的先后顺序决定的

如下,两个 Filter

public class Filter1 implements Filter {
    
    

    @Override
    public void init(FilterConfig filterConfig) {
    
    
        System.out.println("Filter1-init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
        System.out.println("-----Filter1放行前-----");
        HttpServletRequest httpServletRequest= (HttpServletRequest) request;
        // 获取当前请求的URL
        System.out.println(httpServletRequest.getRequestURL());
        // 放行,调用下一个过滤器或者访问资源
        chain.doFilter(request, response);
        System.out.println("-----Filter1放行后-----");
    }

    @Override
    public void destroy() {
    
    
        System.out.println("destroy");
    }
}
public class Filter2 implements Filter {
    
    

    @Override
    public void init(FilterConfig filterConfig) {
    
    
        System.out.println("Filter2-init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
        System.out.println("-----Filter2放行前-----");
        HttpServletRequest httpServletRequest= (HttpServletRequest) request;
        // 获取当前请求的URL
        System.out.println(httpServletRequest.getRequestURL());
        // 放行,调用下一个过滤器或者访问资源
        chain.doFilter(request, response);
        System.out.println("-----Filter2放行后-----");
    }

    @Override
    public void destroy() {
    
    
        System.out.println("destroy");
    }
}

以下是映射配置

<filter>
    <filter-name>Filter1</filter-name>
    <filter-class>com.example.filter.Filter1</filter-class>
</filter>
<filter>
    <filter-name>Filter2</filter-name>
    <filter-class>com.example.filter.Filter2</filter-class>
</filter>

<!--Filter2的mapping在前-->
<filter-mapping>
    <filter-name>Filter2</filter-name>
    <!--指定拦截指定的ServletName-->
    <servlet-name>Servlet2</servlet-name>
</filter-mapping>
<filter-mapping>
    <filter-name>Filter1</filter-name>
    <!--指定拦截指定路径的资源URL-->
    <url-pattern>/aa/*</url-pattern>
    <!--指定拦截指定的ServletName-->
    <servlet-name>Servlet2</servlet-name>
</filter-mapping>
<filter-mapping>
    <filter-name>Filter2</filter-name>
    <!--指定拦截指定路径的资源URL-->
    <url-pattern>/aa/*</url-pattern>
</filter-mapping>

可以看到 Filter2mapping 在前,因此 Filter2 将先被执行,但是在资源访问完毕返回时,则是倒序执行

参考:https://blog.csdn.net/weixin_43767015/article/details/113779782

猜你喜欢

转载自blog.csdn.net/weixin_38192427/article/details/115057833