资讯专栏INFORMATION COLUMN

详谈 Filter 过滤器

wind5o / 1655人阅读

摘要:元素用于指定过滤器的完整的限定类名。除此之外,过滤器不会被调用。参数用于访问后续过滤器。还可以为指定目标资源为某个,例如当用户访问时,会执行名字为的,这时会执行过滤器。防止中文乱码过滤器项目使用框架时。

文章首发在CSDN博客,转载请务必注明以下所有链接,否则考虑法律追究责任。

CSDN地址:http://blog.csdn.net/tzs_1041218129/article/details/53345485

个人博客地址:www.54tianzhisheng.cn/Blog/html/filter.html (阅读效果最好)

pdf 版本:www.54tianzhisheng.cn/Blog/pdf/filter.pdf

所有文章干货都在:https://github.com/zhisheng17/zhisheng17.github.io

更多精彩博客还请关注我的微信公众号:猿blog

1、简介

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

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

  Filter功能:

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

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

2、如何实现拦截

  Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:

调用目标资源之前,让一段代码执行。

是否调用目标资源(即是否让用户访问web资源)。

  web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。 

3、Filter开发两步走

编写java类实现Filter接口,并实现其doFilter方法。 

在 web.xml 文件中使用元素对编写的filter类进行注册,并设置它所能拦截的资源。

  web.xml配置各节点介绍:

用于为过滤器指定一个名字,该元素的内容不能为空。
元素用于指定过滤器的完整的限定类名。
元素用于为过滤器指定初始化参数,它的子元素指定参数的名字,指定参数的值。
在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。

元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
子元素用于设置filter的注册名称。该值必须是在元素中声明过的过滤器的名字
设置 filter 所拦截的请求路径(过滤器关联的URL样式)
指定过滤器所拦截的Servlet名称。
指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。

 子元素可以设置的值及其意义:
REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。

4、Filter链

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

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

多个过滤器执行顺序

一个目标资源可以指定多个过滤器,过滤器的执行顺序是在web.xml文件中的部署顺序:


    myFilter1
    cn.cloud.filter.MyFilter1
  
  
    myFilter1
    /index.jsp
  
  
    myFilter2
    cn. cloud.filter.MyFilter2
  
  
    myFilter2
    /index.jsp
  

MyFilter1

public class MyFilter1 extends HttpFilter {
    public void doFilter(HttpServletRequest request, HttpServletResponse response,
           FilterChain chain) throws IOException, ServletException {
       System.out.println("filter1 start...");
       chain.doFilter(request, response);//放行,执行MyFilter2的doFilter()方法
       System.out.println("filter1 end...");
    }
}

MyFilter2

public class MyFilter2 extends HttpFilter {
    public void doFilter(HttpServletRequest request, HttpServletResponse response,
           FilterChain chain) throws IOException, ServletException {
       System.out.println("filter2 start...");
       chain.doFilter(request, response);//放行,执行目标资源
       System.out.println("filter2 end...");
    }
}

    This is my JSP page. 

index.jsp

<%System.out.println("index.jsp"); %>

当有用户访问index.jsp页面时,输出结果如下:

filter1 start...
filter2 start...
index.jsp
filter2 end...
filter1 end...

5、Filter的生命周期

public void init(FilterConfig filterConfig) throws ServletException;//初始化

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

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;//拦截请求

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

public void destroy();//销毁

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

6、FilterConfig接口

  用户在配置filter时,可以使用为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得以下内容:

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

7、FilterChain

doFilter()方法的参数中有一个类型为FilterChain的参数,它只有一个方法:doFilter(ServletRequest,ServletResponse)

doFilter() 方法的放行,让请求流访问目标资源!其实调用该方法的意思是,当前 Filter 放行了,但不代表其他过滤器也放行。一个目标资源上,可能部署了多个过滤器,所以调用 FilterChain 类的 doFilter() 方法表示的是执行下一个过滤器的 doFilter() 方法,或者是执行目标资源!

如果当前过滤器是最后一个过滤器,那么调用 chain.doFilter() 方法表示执行目标资源,而不是最后一个过滤器,那么 chain.doFilter() 表示执行下一个过滤器的 doFilter() 方法。

8、过滤器的应用场景

执行目标资源之前做预处理工作,例如设置编码,这种试通常都会放行,只是在目标资源执行之前做一些准备工作;

通过条件判断是否放行,例如校验当前用户是否已经登录,或者用户IP是否已经被禁用;

在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理

设置目标资源

在web.xml文件中部署Filter时,可以通过“*”来执行目标资源:


       myfilter
       /*
    

特性与Servlet完全相同!通过这一特性,可以在用户访问敏感资源时,执行过滤器,例如:/admin/*,可以把所有管理员才能访问的资源放到/admin路径下,这时可以通过过滤器来校验用户身份。

还可以为指定目标资源为某个Servlet,例如:


       myservlet
       cn.cloud.servlet.MyServlet
    
    
       myservlet
       /abc
    
    
       myfilter
       cn.cloud.filter.MyFilter
    
    
       myfilter
       myservlet
    

当用户访问http://localhost:8080/filtertest/abc时,会执行名字为myservlet的Servlet,这时会执行过滤器。

9、四种拦截方式

写一个过滤器,指定过滤的资源为b.jsp,然后在浏览器中直接访问b.jsp,会发现过滤器执行了.但是,当在a.jsp中request.getRequestDispathcer(“/b.jsp”).forward(request,response)时,就不会再执行过滤器了!也就是说,默认情况下,只能直接访问目标资源才会执行过滤器,而forward执行目标资源,不会执行过滤器!

public class MyFilter extends HttpFilter {
    public void doFilter(HttpServletRequest request,
           HttpServletResponse response, FilterChain chain)
           throws IOException, ServletException {
       System.out.println("myfilter...");
       chain.doFilter(request, response);
    }
}

       myfilter
       cn.itcast.filter.MyFilter
    
    
       myfilter
       /b.jsp
    

   

b.jsp

a.jsp

<% request.getRequestDispatcher("/b.jsp").forward(request, response); %>

在浏览器输入:

http://localhost:8080/filtertest/b.jsp 直接访问b.jsp时,会执行过滤器内容;

http://localhost:8080/filtertest/a.jsp 访问a.jsp,但a.jsp会forward到b.jsp,这时就不会执行过滤器!

过滤器有四种拦截方式!分别是:REQUESTFORWARDINCLUDEERROR

REQUEST:直接访问目标资源时执行过滤器。包括:在地址栏中直接访问、表单提交、超链接、重定向,只要在地址栏中可以看到目标资源的路径,就是REQUEST

FORWARD:转发访问执行过滤器。包括RequestDispatcher#forward()方法、标签都是转发访问

INCLUDE:包含访问执行过滤器。包括RequestDispatcher#include()方法、标签都是包含访问

ERROR:当目标资源在web.xml中配置为中时,并且真的出现了异常,转发到目标资源时,会执行过滤器。

可以在中添加0~n个子元素,来说明当前访问的拦截方式。

如:

 
       myfilter
       /b.jsp
       REQUEST
       FORWARD
    

最为常用的就是REQUEST和FORWARD两种拦截方式,而INCLUDE和ERROR都比较少用!其中INCLUDE比较好理解,ERROR方式不易理解,下面给出ERROR拦截方式的例子:


       myfilter
       /b.jsp
       ERROR
    
    
       500
       /b.jsp
    

  

a.jsp

<% if(true) throw new RuntimeException("嘻嘻~"); %>

10、Filter使用案例

  1、使用Filter验证用户登录安全控制

  前段时间参与维护一个项目,用户退出系统后,再去地址栏访问历史,根据url,仍然能够进入系统响应页面。我去检查一下发现对请求未进行过滤验证用户登录。添加一个filter搞定问题!

  先在web.xml配置


    SessionFilter
    com.action.login.SessionFilter
    
        logonStrings
        /project/index.jsp;login.do
    
    
        includeStrings
        .do;.jsp
    
    
        redirectPath
        /index.jsp
    
    
        disabletestfilter
        N
    


    SessionFilter
    /*

  接着编写FilterServlet:

package com.action.login;

import java.io.IOException;

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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
 *    判断用户是否登录,未登录则退出系统
 */
public class SessionFilter implements Filter {

    public FilterConfig config;

    public void destroy() {
        this.config = null;
    }

    public static boolean isContains(String container, String[] regx) {
        boolean result = false;

        for (int i = 0; i < regx.length; i++) {
            if (container.indexOf(regx[i]) != -1) {
                return true;
            }
        }
        return result;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest hrequest = (HttpServletRequest)request;
        HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) response);

        String logonStrings = config.getInitParameter("logonStrings");        // 登录登陆页面
        String includeStrings = config.getInitParameter("includeStrings");    // 过滤资源后缀参数
        String redirectPath = hrequest.getContextPath() + config.getInitParameter("redirectPath");// 没有登陆转向页面
        String disabletestfilter = config.getInitParameter("disabletestfilter");// 过滤器是否有效

        if (disabletestfilter.toUpperCase().equals("Y")) {    // 过滤无效
            chain.doFilter(request, response);
            return;
        }
        String[] logonList = logonStrings.split(";");
        String[] includeList = includeStrings.split(";");

        if (!this.isContains(hrequest.getRequestURI(), includeList)) {// 只对指定过滤参数后缀进行过滤
            chain.doFilter(request, response);
            return;
        }

        if (this.isContains(hrequest.getRequestURI(), logonList)) {// 对登录页面不进行过滤
            chain.doFilter(request, response);
            return;
        }

        String user = ( String ) hrequest.getSession().getAttribute("useronly");//判断用户是否登录
        if (user == null) {
            wrapper.sendRedirect(redirectPath);
            return;
        }else {
            chain.doFilter(request, response);
            return;
        }
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        config = filterConfig;
    }
}

  这样既可完成对用户所有请求,均要经过这个Filter进行验证用户登录。

  2、防止中文乱码过滤器

  项目使用spring框架时。当前台JSP页面和JAVA代码中使用了不同的字符集进行编码的时候就会出现表单提交的数据或者上传/下载中文名称文件出现乱码的问题,那就可以使用这个过滤器。


    encoding
    org.springframework.web.filter.CharacterEncodingFilter
    
        encoding
        UTF-8
    
    
        forceEncoding
        false
    


    encoding
    /*

  3、Spring+Hibernate的OpenSessionInViewFilter控制session的开关

  当 hibernate+spring 配合使用的时候,如果设置了lazy=true(延迟加载),那么在读取数据的时候,当读取了父数据后,hibernate 会自动关闭 session,这样,当要使用与之关联数据、子数据的时候,系统会抛出lazyinit的错误,这时就需要使用 spring 提供的 OpenSessionInViewFilter 过滤器。

  OpenSessionInViewFilter主要是保持 Session 状态直到 request 将全部页面发送到客户端,直到请求结束后才关闭 session,这样就可以解决延迟加载带来的问题。

  注意:OpenSessionInViewFilter 配置要写在struts2的配置前面。因为 tomcat 容器在加载过滤器的时候是按照顺序加载的,如果配置文件先写的是 struts2 的过滤器配置,然后才是 OpenSessionInViewFilter 过滤器配置,所以加载的顺序导致,action 在获得数据的时候 session 并没有被 spring 管理。



    OpenSessionInViewFilter
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    
        sessionFactoryBeanName
        sessionFactory
    
    
        singleSession
        true
    


    OpenSessionInViewFilter
    *.do

  4、Struts2的web.xml配置

  项目中使用Struts2同样需要在web.xml配置过滤器,用来截取请求,转到Struts2的Action进行处理。

  注意:如果在2.1.3以前的Struts2版本,过滤器使用org.apache.struts2.dispatcher.FilterDispatcher。否则使用org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter。从Struts2.1.3开始,将废弃ActionContextCleanUp过滤器,而在StrutsPrepareAndExecuteFilter过滤器中包含相应的功能。

  三个初始化参数配置:

config参数:指定要加载的配置文件。逗号分割。

actionPackages参数:指定Action类所在的包空间。逗号分割。

configProviders参数:自定义配置文件提供者,需要实现ConfigurationProvider接口类。逗号分割。



    struts2
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter


    struts2
    *.do

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69829.html

相关文章

  • ES6指北【6】——详谈解构赋值【附赠练习题】

    摘要:指北详谈解构赋值附赠练习题一何谓解构赋值基本概念首先我们看一下给的定义解构赋值语法是一个表达式,这使得可以将值从数组或属性从对象提取到不同的变量中从定义中,我们可以发现解构赋值的作用是对变量进行赋值主要通过两个方面实现这个作用数组将数组中的 ES6指北【6】——详谈解构赋值【附赠练习题】 一、何谓解构赋值? 1. 基本概念 首先我们看一下MDN给的定义 解构赋值语法是一个 Javasc...

    sorra 评论0 收藏0
  • 详谈js防抖和节流

    摘要:本文会分别介绍什么是防抖和节流,它们的应用场景,和实现方式。防抖和节流都是为了解决短时间内大量触发某函数而导致的性能问题,比如触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。 本文由小芭乐发表 0. 引入 首先举一个例子: 模拟在输入框输入后做ajax查询请求,没有加入防抖和节流的效果,这里附上完整可执行代码: 没有防抖 ...

    shevy 评论0 收藏0
  • JavaScript数据类型----原始类型与引用类型(object)详谈

    摘要:做个小小的延伸在这个栗子中,两个对象都有一条相同的值,但这两个对象却不相等,为什么呢因为两个对象分别引用的是存放在堆内存中的个不同的对象,故变量和的值引用地址也是不一样的。 JavaScript中,有七种内置类型:6种原始类型和引用类型,他们分别是: 6种原始类型(基本类型): 空值(null) 未定义(undefined) 布尔值(boolean) 数字(number) 字符串(s...

    tomlingtm 评论0 收藏0
  • 用Python写了个检测文章抄袭,详谈去重算法原理

    摘要:中文网页的一大特点就是天下文章一大抄,各种博文新闻几乎一字不改或稍作修改就被网站发表了。这个特点,很适合这个百度算法。但是,实际中个别字的修改,会导致被转载的最长的那句话不一样,从而其值也不一样了,最终结果是,准确率很高,召回率较低。 在互联网出现之前,抄很不方便,一是源少,而是发布渠道少;而在互联网出现之后,抄变得很简单,铺天盖地的源源源不断,发布渠道也数不胜数,博客论坛甚至是自建网...

    blair 评论0 收藏0
  • 支付宝小程序之连接后端+详谈引入阿里icon图标

    摘要:背景第一天练习支付宝小程序的时候写好了一个简单的页面后续是新增重置按钮连接后端接口然后加入图标网上看了很多小程序加图标例子说实话没有单独能成功的我也不记得结合了多少个才成功了还是自己写了篇文章记录下引入流程可能有点啰嗦但是比较详细连接后端接 背景:第一天练习支付宝小程序的时候写好了一个简单的页面.后续是新增重置按钮,连接后端接口.然后加入icon图标.网上看了很多小程序加icon图标例...

    biaoxiaoduan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<