一、Filter的介绍及使用

什么是过滤器?

与Servlet相似,过滤器是一些web应用程序组件,可以绑定到一个web应用程序中。但是与其他web应用程序组件不同的是,过滤器是"链"在容器的处理过程中的。这就意味着它们会在servlet处理器之前访问一个进入的请求,并且在外发响应信息返回到客户前访问这些响应信息。这种访问使得过滤器可以检查并修改请求和响应的内容。

过滤器适用于那些地方?

l为一个web应用程序的新功能建立模型(可被添加到web应用程序中或者从web应用程序中删除而不需要重写基层应用程序代码);

l向过去的代码添加新功能。

过滤器放在容器结构的什么位置?

过滤器放在web资源之前,可以在请求抵达它所应用的web资源(可以是一个Servlet、一个Jsp页面,甚至是一个HTML页面)之前截获进入的请求,并且在它返回到客户之前截获输出请求。Filter:用来拦截请求,处于客户端与被请求资源之间,目的是重用代码。Filter链,在web.xml中哪个先配置,哪个就先调用。在filter中也可以配置一些初始化参数。

Java中的Filter并不是一个标准的Servlet,它不能处理用户请求,也不能对客户端生成响应。主要用于对HttpServletRequest进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。

Filter有如下几个用处:

l在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。

l根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。

l在HttpServletResponse到达客户端之前,拦截HttpServletResponse。

l根据需要检查HttpServletResponse,可以修改HttpServletResponse头和数据。

Filter有如下几个种类:

l用户授权的Filter: Filter负责检查用户请求,根据请求过滤用户非法请求。

l日志Filter:详细记录某些特殊的用户请求。

l负责解码的Filter:包括对非标准编码的请求解码。

l能改变XML内容的XSLTFilter等。

一个Filter可负责拦截多个请求或响应:一个请求或响应也可被多个请求拦截。

创建一个Filter只需两个步骤:
(1)创建Filter处理类:

(2)在web.xml文件中配置Filter。
创建Filter必须实现javax.servlet.Filter接口,在该接口中定义了三个方法。
• void init(FilterConfig config): 用于完成Filter的初始化。
• void destroy(): 用于Filter销毁前,完成某些资源的回收。
• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理。

过滤器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驱动。在servlet2.4中,过滤器同样可以用于请求分派器,但须在web.xml中声明,<dispatcher>INCLUDE或FORWARD或REQUEST或ERROR</dispatcher>该元素位于filter-mapping中。

Filter常用的场景:

例一、日志的记录,当有请求到达时,在该过滤器中进行日志的记录。处理完成后,进入后续的Filter或者处理。

步骤1:编写Filter类

package test.filter;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletContext;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

publicclass LogFilter implements Filter {

private FilterConfigconfig;

//实现初始化方法

publicvoid init(FilterConfig config) {

this.config = config;

}

//实现销毁方法

publicvoid destroy() {

this.config =null;

}

publicvoid doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) {

//获取ServletContext对象,用于记录日志

ServletContext context =this.config.getServletContext();

long before = System.currentTimeMillis();

System.out.println("开始过滤... ");

//将请求转换成HttpServletRequest请求

HttpServletRequest hrequest = (HttpServletRequest) request;

//记录日志

context.log("Filter已经截获到用户的请求的地址: " + hrequest.getServletPath());

try {

// Filter只是链式处理,请求依然转发到目的地址。

chain.doFilter(request, response);

}catch (Exception e) {

e.printStackTrace();

}

long after = System.currentTimeMillis();

//记录日志

context.log("过滤结束");

//再次记录日志

context.log("请求被定位到" + ((HttpServletRequest) request).getRequestURI()

+"所花的时间为: " + (after - before));

}

}

在上面的请求Filter中,仅在日志中记录请求的URL,对所有的请求都执行chain.doFilter(request,reponse)方法,当Filter对请求过滤后,依然将请求发送到目的地址。

步骤2:在web.xml中配置Filter

<!--定义Filter -->

<filter>

<!-- Filter的名字 -->

<filter-name>log</filter-name>

<!-- Filter的实现类 -->

<filter-class> test.filter.LogFilter</filter-class>

</filter>

<!--定义Filter 拦截地址 -->

<filter-mapping>

<!-- Filter的名字 -->

<filter-name>log</filter-name>

<!-- Filter负责拦截的URL -->

<url-pattern>/filter/*</url-pattern>

</filter-mapping>

通过上述步骤的操作,此时就可以通过URI进行访问。具体访问后会在log文件中的localhost文件中产生具体的访问日志。如下所示:

2010-12-28 21:12:50 org.apache.catalina.core.ApplicationContext log

信息:请求被定位到/examples/jsp/jsp2/el/basic-arithmetic.jsp所花的时间为: 0

2010-12-28 21:14:55 org.apache.catalina.core.ApplicationContext log

信息: Filter已经截获到用户的请求的地址: /jsp/jsp2/el/basic-comparisons.jsp

2010-12-28 21:14:56 org.apache.catalina.core.ApplicationContext log

信息:过滤结束

例二、进行编码的修正,当有新的请求时,需要将用户传送过来的字符进行重新编码,以使其可以满足服务器的编码格式。

1、编写EncodingFilter类

package test.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletContext;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

publicclass EncodingFilterimplements Filter {

private FilterConfig filterConfig =null;

private String encoding =null;

//实现销毁方法

publicvoid destroy() {

encoding = null;

}

//进行具体的过滤

publicvoid doFilter(ServletRequest request, ServletResponse response,

FilterChain chain)throws IOException, ServletException {

//获取ServletContext对象,用于记录日志

ServletContext context =this.filterConfig.getServletContext();

context.log("开始设置编码格式");

String encoding = getEncoding();

if (encoding == null){

encoding ="gb2312";

}

//在请求里设置上指定的编码

request.setCharacterEncoding(encoding);

chain.doFilter(request, response);

context.log("成功设置了编码格式");

}

//初始化配置

publicvoid init(FilterConfig filterConfig)throws ServletException {

this.filterConfig = filterConfig;

this.encoding = filterConfig.getInitParameter("encoding");

}

private String getEncoding() {

returnthis.encoding;

}

}

步骤2:在web.xml中配置Filter

<!--定义Filter -->

<filter>

<!-- Filter的名字 -->

<filter-name>encoding</filter-name>

<!-- Filter的实现类 -->

<filter-class> test.filter.EncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>gb2312</param-value>

</init-param>

</filter>

<!--定义Filter 拦截地址 -->

<filter-mapping>

<!-- Filter的名字 -->

<filter-name> encoding </filter-name>

<!-- Filter负责拦截的URL -->

<url-pattern>/encode/*</url-pattern>

</filter-mapping>

通过上述步骤的操作,此时就可以通过URI进行访问。

例三、用户权限的认证,当用户发送请求时,可以对用户的身份信息进行验证,如果能够通过验证则接下来再进行其它操作,否则直接不进入下一步的处理。

1、编写身份认证SecurityFilter类

package test.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletContext;

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.HttpSession;

publicclass SecurityFilterimplements Filter {

private FilterConfig filterConfig;

//初始化方法实现

@Override

publicvoid init(FilterConfig filterConfig)throws ServletException {

this.filterConfig = filterConfig;

}

//身份认证的过滤

@Override

publicvoid doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

throws IOException, ServletException {

ServletContext context =this.filterConfig.getServletContext();

HttpServletRequest req = (HttpServletRequest) request;

HttpServletResponse res = (HttpServletResponse) response;

HttpSession session = req.getSession();

//登录后才能进入下一步处理,否则直接进入错误提示页面

if (session.getAttribute("username") !=null) {

context.log("身份认证通过,进入下一步处理 ");

chain.doFilter(request, response);

}else {

context.log("身份认证失败,直接返回");

res.sendRedirect("../failure.jsp");

}

}

//实现销毁方法

@Override

publicvoid destroy() {

this.filterConfig = null;

}

}

步骤2:在web.xml中配置Filter

<!--定义Filter -->

<filter>

<!-- Filter的名字 -->

<filter-name>security</filter-name>

<!-- Filter的实现类 -->

<filter-class> test.filter.SecurityFilter</filter-class>

</filter>

<!--定义Filter 拦截地址 -->

<filter-mapping>

<!-- Filter的名字 -->

<filter-name> security </filter-name>

<!-- Filter负责拦截的URL -->

<url-pattern>/security/*</url-pattern>

</filter-mapping>

通过上述步骤的操作,此时就可以通过URI进行访问。此时如果能够取得Session中的username值时,会直接进入下一步处理,否则直接进入错误页面。

二、过滤链FilterChain

两个过滤器,EncodingFilter负责设置编码,SecurityFilter负责控制权限,服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后一次执行其中的doFilter()方法。执行的顺序就如下图所示,执行第一个过滤器的chain.doFilter()之前的代码,第二个过滤器的chain.doFilter()之前的代码,请求的资源,第二个过滤器的chain.doFilter()之后的代码,第一个过滤器的chain.doFilter()之后的代码,最后返回响应。

执行的代码顺序是:

l执行EncodingFilter.doFilter()中chain.doFilter()之前的部分;request.setCharacterEncoding(encoding);

l执行SecurityFilter.doFilter()中chain.doFilter()之前的部分:判断用户是否已登录。

l如果用户已登录,则访问请求的资源。

l如果用户未登录,则页面重定向到:/failure.jsp。

l执行SecurityFilter.doFilter()中chain.doFilter()之后的部分:这里没有代码。

l执行EncodingFilter.doFilter()中chain.doFilter()之后的部分:写入已经完成的日志。

过滤链的好处是,执行过程中任何时候都可以打断,只要不执行chain.doFilter()就不会再执行后面的过滤器和请求的内容。而在实际使用时,就要特别注意过滤链的执行顺序问题,像EncodingFilter就一定要放在所有Filter之前,这样才能确保在使用请求中的数据前设置正确的编码

Filter及FilterChain的详解的更多相关文章

  1. SpringBoot初始教程之Servlet、Filter、Listener配置详解

    1.介绍 通过之前的文章来看,SpringBoot涵盖了很多配置,但是往往一些配置是采用原生的Servlet进行的,但是在SpringBoot中不需要配置web.xml的 因为有可能打包之后是一个ja ...

  2. 基于jquery的has()方法以及与find()方法以及filter()方法的区别详解

    has(selector选择器或DOM元素)   将匹配元素集合根据选择器或DOM元素为条件,检索该条件在每个元素的后代中是否存在,将符合条件的的元素构成新的结果集. 下面举一个例子: <ul& ...

  3. js数组中indexOf/filter/forEach/map/reduce详解

    今天在网上看到一篇帖子,如题: 出处:前端开发博客 (http://caibaojian.com/5-array-methods.html) 在ES5中一共有9个Array方法,分别是: Array. ...

  4. JS Array常用方法indexOf/filter/forEach/map/reduce详解

    Array共有九个方法   Array.prototype.indexOf Array.prototype.lastIndexOf Array.prototype.every Array.protot ...

  5. 5个现在就该使用的数组Array方法: indexOf/filter/forEach/map/reduce详解(转)

    ECMAScript5标准发布于2009年12月3日,它带来了一些新的,改善现有的Array数组操作的方法.然而,这些新奇的数组方法并没有真正流行起来的,因为当时市场上缺乏支持ES5的浏览器.     ...

  6. HBase Filter 过滤器之RowFilter详解

    前言:本文详细介绍了HBase RowFilter过滤器Java&Shell API的使用,并贴出了相关示例代码以供参考.RowFilter 基于行键进行过滤,在工作中涉及到需要通过HBase ...

  7. HBase Filter 过滤器之FamilyFilter详解

    前言:本文详细介绍了 HBase FamilyFilter 过滤器 Java&Shell API 的使用,并贴出了相关示例代码以供参考.FamilyFilter 基于列族进行过滤,在工作中涉及 ...

  8. HBase Filter 过滤器之QualifierFilter详解

    前言:本文详细介绍了 HBase QualifierFilter 过滤器 Java&Shell API 的使用,并贴出了相关示例代码以供参考.QualifierFilter 基于列名进行过滤, ...

  9. HBase Filter 过滤器之 ValueFilter 详解

    前言:本文详细介绍了 HBase ValueFilter 过滤器 Java&Shell API 的使用,并贴出了相关示例代码以供参考.ValueFilter 基于列值进行过滤,在工作中涉及到需 ...

随机推荐

  1. [codeforces934D]A Determined Cleanup

    [codeforces934D]A Determined Cleanup 试题描述 In order to put away old things and welcome a fresh new ye ...

  2. 洛谷 P4139 上帝与集合的正确用法 解题报告

    P4139 上帝与集合的正确用法 题目描述 根据一些书上的记载,上帝的一次失败的创世经历是这样的: 第一天, 上帝创造了一个世界的基本元素,称做"元". 第二天, 上帝创造了一个新 ...

  3. gitHub优秀android项目

    转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介绍那些不错个性化的View,包括ListView.ActionBar.M ...

  4. Intel与Motorola区别

    Intel低字节在前 Motorola高字节在前    在进行CAN总线通信设计或者测试过 程中,经常看到CAN总线信号的编码格式有两种定义:Intel格式与Motorola格式.究竟两种编码格式有什 ...

  5. 洛谷 P1463 [SDOI2005]反素数ant && codevs2912反素数

    题目描述 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4. 如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数.例如,整数1,2,4,6 ...

  6. Hibernate中的延迟加载及fetch

    Hibernate中的延迟加载 1.类级别的查询策略: lazy  :  true(默认值) false(立即加载) 2.多对一关联的查询策略: lazy:  proxy(默认值) no-proxy ...

  7. FreeRTOS系列第2篇---FreeRTOS入门指南【转】

    转自:http://blog.csdn.net/zhzht19861011/article/details/49819309 版权声明:本文为博主原创文章,未经博主允许不得转载.联系邮箱:zhzhch ...

  8. UVALIVE 2927 "Shortest" pair of paths

    裸的费用流.一开始因为这句话还觉得要拆点 样例行不通不知道这句话干啥用的.Further, the company cannot place the two chemicals in same dep ...

  9. spark streaming 异常No output streams registered, so nothing to execute

    实现spark streaming demo时,代码: public static void main (String[] args) { SparkConf conf = new SparkConf ...

  10. 【Android开发日记】之入门篇(二)——调试

    程序员有一半的时间花在测试BUG身上,而作为一个程序员遇上BUG是不可避免的事情.所以掌握好调试BUG的技术就显得至关重要.接下来我来讲述调试的几个要点. 一.调试机器的选择(模拟器) eclipse ...