Filter是拦截Request请求的对象:在用户的请求访 问资源前处理ServletRequest以及ServletResponse,它可 用于日志记录、加解密、Session检查、图像文件保护 等。通过Filter可以拦截处理某个资源或者某些资源。 Filter的配置可以通过Annotation或者部署描述来完成。 当一个资源或者某些资源需要被多个Filter所使用到, 且它的触发顺序很重要时,只能通过部署描述来配置。

一.Filter API

1. Filter 相关接口,包含Filter, FilterConfig, FilterChain

  Filter的实现必须继承javax.servlet.Filter接口。这个 接口包含了Filter的3个生命周期:init、doFilter、 destroy。

  Servlet容器初始化Filter时,会触发Filter的init方 法,一般来说是在应用开始时。也就是说,init方法并 不是在该Filter相关的资源使用到时才初始化的,而且 这个方法只调用一次,用于初始化Filter。init方法的定 义如下:

  1. oid init(FilterConfig filterConfig)

注意: FilterConfig实例是由Servlet容器传入init方法中的.

  当Servlet容器每次处理Filter相关的资源时,都会调 用该Filter实例的doFilter方法。Filter的doFilter方法包含 ServletRequest、ServletResponse、FilterChain这3个参 数。

  doFilter的定义如下:

  1. void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain)

  接下来,说明一下doFilter的实现中访问 ServletRequet、ServletResponse。这也就意味着允许给 ServletRequest增加属性或者增加Header。当然也可以修 饰ServletRequest或者ServletRespone来改变它们的行 为。

  在Filter的doFilter的实现中,最后一行需要调用 FilterChain中的doFilter方法。注意Filter的doFilter方法 里的第3个参数,就是filterChain的实例:

  1. filterChain.doFilter(request, response)

  一个资源可能需要被多个Filter关联到(更专业一 点来说,这应该叫作Filter链条),这时Filter.doFilter() 的方法将触发Filter链条中下一个Filter。只有在Filter链 条中最后一个Filter里调用的FilterChain.doFilter(),才会 触发处理资源的方法。

  如果在Filter.doFilter()的实现中,没有在结尾处调 用FilterChain.doFilter()的方法,那么该Request请求中 止,后面的处理就会中断。

注意: FilterChain接口中,唯一的方法就是doFilter。该方法与Filter中的 doFilter的定义是不一致的:在FilterChaing中,doFilter方法只有两个参 数,但在Filter中,doFilter方法有三个参数。

  Filter接口中,最后一个方法是destroy,它的定义 如下:

  1. Void destroy()

  该方法在Servlet容器要销毁Filter时触发,一般在应 用停止的时候进行调用。

  除非Filter在部署描述中被多次定义到,否则Servlet 窗口只会为每个Filter创建单一实例。由于Serlvet/JSP的 应用通常要处理用户并发请求,此时Filter实例需要同 时被多个线程所关联到,因此需要非常小心地处理多线 程问题。

三. Filter配置

  当完成Filter的实现后,就可以开始配置Filter了。 Filter的配置需要如下步骤:

  • 确认哪些资源需要使用这个Filter拦截处理。
  • 配置Filter的初始化参数值,这些参数可以在Filter的 init方法中读取到;
  • 给Filter取一个名称。一般来说,这个名称没有什么 特别的含义,但在一些特殊的情况下,这个名字十 分有用。例如,要记录Filter的初始化时间,但这个 应用中有许多的Filter,这时它就可以用来识别Filter 了。

  FilterConfig接口允许通过它的getServletContext的 方法来访问ServletContext:

  1. ServletContext getServletContext(

  如果配置了Filter的名字,在FilterConfig的 getFilterName中就可以获取Filter的名字。getFilterName 的定义如下:

  1. java.lang.String getFilterName()

  当然,最重要的还是要获取到开发者或者运维给 Filter配置的初始化参数。为了获取这些初始化参数, 需要用到FilterConfig中的两个方法,第一个方法是 getParameterNames:

  1. java.util.Enumeration<java.lang.String> getInitParameterNames()

  这个方法返回Filter参数名字的Enumeration对象。 如果没有给这个Filter配置任何参数,该方法返回的是 空的Enumeration对象。

  第二个方法是getParameter:

  1. java.lang.String getInitParameter(java.lang.String parameterName)

  有两种方法可以配置Filter:一种是通过WebFilter 的Annotation来配置Filter,另一种是通过部署描述来注 册。使用@WebFilter的方法,只需要在Filter的实现类 中增加一个注解即可,不需要重复地配置部署描述。当 然,此时要修改配置参数,就需要重新构建Filter实现 类了。换句话说,使用部署描述意味着修改Filter配置 只要修改一下文本文件就可以了。

  使用@WebFilter,你需要熟悉下表中所列出来的 参数,这些参数是在WebFilter的Annotation里定义的。 所有参数都是可选的。

WebFilter的属性
属性 描述
asyncSupported Filter是否支持异步操作
description Filter的描述
dispatcerTypes Filter所生效范围
displayName Filter的显示名
filterName Filter的名称
initParams Filter的初始化参数
largeIcon Filter的大图名称
servletName Filter所生效的Servlet名称
smallIcon Filter的小图名称
urlPatterns Filter所生效的URL路径
value Filter所生效的URL路径

 

三. 示例1: 日志 Filter

  作为第1个例子,将做一个简单的Filter:在app09a 的应用中把Request请求的URL记录到日志文本文件 中。日志文本文件名通过Filter的初始化参数来配置。 此外,日志的每条记录都会有一个前缀,该前缀也由 Filter初始化参数来定义。通过日志文件,可以获得许 多有用的信息,例如在应用中哪些资源访问最频繁; Web站点在一天中的哪个时间段访问量最多。

   这个Filter的类名叫LoggingFilter。 一般情况下,Filter的类名都以*Filter结尾。

  1. package filter;
  2.  
  3. import java.io.File;
  4. import java.io.FileNotFoundException;
  5. import java.io.IOException;
  6. import java.io.PrintWriter;
  7. import java.util.Date;
  8. import javax.servlet.Filter;
  9. import javax.servlet.FilterChain;
  10. import javax.servlet.FilterConfig;
  11. import javax.servlet.ServletException;
  12. import javax.servlet.ServletRequest;
  13. import javax.servlet.ServletResponse;
  14. import javax.servlet.annotation.WebFilter;
  15. import javax.servlet.annotation.WebInitParam;
  16. import javax.servlet.http.HttpServletRequest;
  17.  
  18. @WebFilter(filterName = "LoggingFilter", urlPatterns = { "/ *" }, initParams = {
  19. @WebInitParam(name = "logFileName", value = "log.txt"), @WebInitParam(name = "prefix", value = "URI: ") })
  20. public class LoggingFilter implements Filter {
  21. private PrintWriter logger;
  22. private String prefix;
  23.  
  24. @Override
  25. public void init(FilterConfig filterConfig) throws ServletException {
  26. prefix = filterConfig.getInitParameter("prefix"); //得到URI
  27. String logFileName = filterConfig.getInitParameter("logFileName");
  28. String appPath = filterConfig.getServletContext().getRealPath("/"); //得到项目路径
  29. // without path info in logFileName, the log file will be
  30. // created in $TOMCAT_HOME/bin
  31. System.out.println("logFileName:" + logFileName);
  32. try {
  33. logger = new PrintWriter(new File(appPath, logFileName)); //打开文件
  34. logger.println("I have the output");
  35. logger.flush();
  36. } catch (FileNotFoundException e) {
  37. e.printStackTrace();
  38. throw new ServletException(e.getMessage());
  39. }
  40. }
  41.  
  42. @Override
  43. public void destroy() {
  44. System.out.println("destroying filter");
  45. if (logger != null) {
  46. logger.close();
  47. }
  48. }
  49.  
  50. @Override
  51. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
  52. throws IOException, ServletException {
  53. System.out.println("LoggingFilter.doFilter");
  54. HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  55. logger.println(new Date() + " " + prefix + httpServletRequest.getRequestURI()); //写入数据
  56. logger.flush(); //刷新缓冲
  57. filterChain.doFilter(request, response); //如果没有doFilter方法,后面发Filter处理就会中断
  58. }
  59.  
  60. }

  下面来仔细分析一下Filter类。 首先,该Filter的类实现了Filter的接口并声明两个 变量:PrintWriter类型的logger和String类型的prefix。

  其中PrintWriter用于记录日志到文本文件,prefix的 字符串用于每条日志的前缀。 Filter的类使用了@WebFilter的Annotation,将两个 参数(logFilteName、prefix)传入到该Filter中.

  在Filter的init方法中,通过FilterConfig里传入的 getInitParameter方法来获取prefix和getFileName的初始 化参数。其中把prefix参数中赋给了类变量prefix, logFileName则用于创建一个PrintWriter

  如果Servlet/JSP应用是通过Servlet/JSP容器启动 的,那么当前应用的工作目录是当前JDK所在的目录。 如果是在Tomcat中,该目录是Tomcat的安装目录。在 应用中创建日志文件,可以通过 ServletContext.getRealPath来获取工作目录,结合应用 工作目录以及初始化参数中的logFilterNmae,就可以得 到日志文件的绝对路径.

  当Filter的init方法被执行时,日志文件就会创建出 来。如果在应用的工作目录中该文件已经存在,那么该 日志文件的内容将会被覆盖。 当应用关闭时,PrintWriter需要被关闭。因此在 Filter的destroy方法中,需要:关闭日志

  Filter的doFilter实现中记录着所有从ServletRequest 到HttpServletRequest的Request,并调用了它的 getRequestURI方法,该方法的返回值将记录通过 PrintWriter的pringln记录下来

  每条记录都有一个时间戳以及前缀,这样可以很方 便地标识每条记录。接下来 Filter的doFilter实现调用 PrintWriter的flush方法以及FilterChain.doFilter,以唤起 资源的调用:

  如果使用Tomcat,Filter的初始化并不会等到第一 个Request请求时才触发进行。这点可以在控制台中打 印出来的logFileName参数值中可以看到。在app09a应 用中通过URL调用test.jsp页面,就可以测试该Filter了

  通过检查日志文件的内容,就可以验证这个Filter 是否运行正常。

四.  示例2:图像文件保护Filter

  本例中的图像文件保护Filter用于在浏览器中输入 图像文件的URL路径时,防止下载图像文件。应用中的 图像文件只有当图像链接在页面中被点击的时候才会显 示。该Filter的实现原理是检查HTTP Header的referer 值。如果该值为null,就意味着当前的请求中没有 referer值,即当前的请求是直接通过输入URL来访问该 资源的。如果资源的Header值为非空,将返回Request 语法的原始页面作为referer值。注意Header的referer的 属性名中,在第2个e以及第3个e中仅有一个r。

   ImageProtectorFilter的Filter实现类,如清单所 示。从WebFilter的Annotation中,可以看到该Filter应用 于所有的.png、.jpg、.gif文件后缀。

ImageProtectorFilter实现类

  1. package filter;
  2.  
  3. import java.io.IOException;
  4. import javax.servlet.Filter;
  5. import javax.servlet.FilterChain;
  6. import javax.servlet.FilterConfig;
  7. import javax.servlet.ServletException;
  8. import javax.servlet.ServletRequest;
  9. import javax.servlet.ServletResponse;
  10. import javax.servlet.annotation.WebFilter;
  11. import javax.servlet.http.HttpServletRequest;
  12.  
  13. //过滤所有以.png , .jpg .gif 为后缀的访问
  14. @WebFilter(filterName = "ImageProtetorFilter", urlPatterns = { "*.png", "*.jpg", "*.gif" })
  15. public class ImageProtectorFilter implements Filter {
  16. @Override
  17. public void init(FilterConfig filterConfig) throws ServletException {
  18. }
  19.  
  20. @Override
  21. public void destroy() {
  22. }
  23.  
  24. @Override
  25. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
  26. throws IOException, ServletException {
  27. //直接输入图片地址访问http://localhost:8080/app09/图片.jpg会被拦截
  28. System.out.println("ImageProtectorFilter");
  29. HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  30. String referrer = httpServletRequest.getHeader("referer");
  31. System.out.println("referrer:" + referrer);
  32. if (referrer != null) {
            
  33. filterChain.doFilter(request, response);
  34. } else {
  35. throw new ServletException("Image not available");
  36. }
  37. }
  38. }

  这里并没有init和destroy方法。其中doFilter方法读 取到Header中的referer值,要确认是要继续处理这个资 源还是给个异常.

  测试该Filter,可以在浏览器中输入如下ULR路 径,尝试访问logo.png图像:

  1. http://localhost:8080/app09/图片.jpg

  接下来,通过image.jsp的页面来访问该图像:

  1. http://localhost:8080/app09/test.jsp

五.示例3:下载计数Filter

  本例子中,下载计数Filter将会示范如何在Filter中 计算资源下载的次数。这个示例特别有用,它将会得到 文档、音频文件的受欢迎程度。作为简单的示例,这里 将数值保存在属性文件中,而不保存在数据库中。其中 资源的ULR路径将作为属性名保存在属性文件中。

  因为我们把值保存在属性文件中,并且Filter可以 被多线程访问,因此涉及线程安全问题。用户访问一个 资源时,Filter需要读取相应的属性值加1,然后保存该 值。如果第二个用户在第一个线程完成前同时访问该资 源,将会发生什么呢?计算值出错。在本例中,读写的 同步锁并不是一个好的解决这个问题的方法,因为它会 导致扩展性问题。

   本示例中,解决这个线程安全问题是通过Queue以 及Executor。

  简而言之,进来的Request请求将会保存在单线程 Executor的队列中。替换这个任务十分方便,因为这是 一个异步的方法,因此你不需要等待该任务结束。 Executor一次从队列中获取一个对象,然后做相应属性 值的增加。由于Executor只在一个线程中使用,因此可 以消除多个线程同时访问一个属性文件的影响。

DownloadCounterFilter实现类

  1. package filter;
  2.  
  3. import java.io.File;
  4. import java.io.FileReader;
  5. import java.io.FileWriter;
  6. import java.io.IOException;
  7. import java.util.Properties;
  8. import java.util.concurrent.ExecutorService;
  9. import java.util.concurrent.Executors;
  10. import javax.servlet.Filter;
  11. import javax.servlet.FilterChain;
  12. import javax.servlet.FilterConfig;
  13. import javax.servlet.ServletException;
  14. import javax.servlet.ServletRequest;
  15. import javax.servlet.ServletResponse;
  16. import javax.servlet.annotation.WebFilter;
  17. import javax.servlet.http.HttpServletRequest;
  18.  
  19. // 拦截所有URL
  20. //书上的是 urlPatterns={"/"},亲测无效
  21. @WebFilter(filterName = "DownloadCounterFilter", urlPatterns = { "*.JPG" })
  22. public class DownloadCounterFilter implements Filter {
  23. // 获取单个线程池的Executor 由于Executor只在一个线程中使用,因此可 以消除多个线程同时访问一个属性文件的影响。
  24. ExecutorService executorService = Executors.newSingleThreadExecutor();
  25. // propeties 属性集
  26. Properties downloadLog;
  27. File logFile;
  28.  
  29. @Override
  30. public void init(FilterConfig filterConfig) throws ServletException {
  31. System.out.println("DownloadCounterFilter");
  32. //获取路径
  33. String appPath = filterConfig.getServletContext().getRealPath("/");
  34. // 获取downLoadLog.txt文件
  35. logFile = new File(appPath, "downloadLog.txt");
  36. if (!logFile.exists()) {
  37. try {
  38. logFile.createNewFile();
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43.  
  44. downloadLog = new Properties();
  45. try {
  46. downloadLog.load(new FileReader(logFile));
  47. } catch (IOException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51.  
  52. @Override
  53. public void destroy() {
  54. executorService.shutdown();
  55. }
  56.  
  57. @Override
  58. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
  59. throws IOException, ServletException {
  60. HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  61. final String uri = httpServletRequest.getRequestURI();
  62. executorService.execute(new Runnable() {
  63. @Override
  64. public void run() {
  65. String property = downloadLog.getProperty(uri);
  66. if (property == null) {
  67. downloadLog.setProperty(uri, "");
  68. } else {
  69. int count = ;
  70. try {
  71. count = Integer.parseInt(property);
  72. } catch (NumberFormatException e) {
  73. // silent
  74. }
  75. count++;
  76. downloadLog.setProperty(uri, Integer.toString(count));
  77. }
  78. try {
  79. downloadLog.store(new FileWriter(logFile), "");
  80. } catch (IOException e) {
  81. }
  82. }
  83. });
  84. filterChain.doFilter(request, response);
  85. }
  86. }

  如果在当前应用的工作目录中不存在 downloadLog.txt文件,这个Filter的init方法就会创建 它

  接着创建Properties对象,并读取该文件.

  注意,Filter的实现类中引用到了 ExecutorService(Executor的子类).

  且当Filter销毁时,会调用ExecutorService的 shutdown方法.

  Filter的doFilter实现中大量地使用到这个Job。每次 URL请求都会调用到ExecutorService的execute方法,然 后才调用FilterChaing.doFilter()。该任务的execute实现 非常好理解:它将URL作为一个属性名,从Properties 实例中获取该属性的值,然后加1,并调用flush方法写 回到指定的日志文件中

  这个Filter可在许多资源上生效,但也可以非常简 单地配置,限定为PDF或者AVI文件资源。

properties文件

六. Filter顺序

  如果多个Filter应用于同一个资源,Filter的触发顺 序将变得非常重要,这时就需要使用部署描述来管理 Filter:指定哪个Filter先被触发。例如:Filter 1需要在 Filter 2前被触发,那么在部署描述中,Filter 1需要配置 在Filter 2之前:

  1. <filter>
  2. <filter-name>Filter1</filter-name>
  3. <filter-class>
  4. the fully-qualified name of the filter class
  5. </filter-class>
  6. </filter>
  7. <filter>
  8. <filter-name>Filter2</filter-name>
  9. <filter-class>
  10. the fully-qualified name of the filter class
  11. </filter-class>
  12. </filter>

通过部署描述之外的配置来指定Filter触发的顺序 是不可能的

JSP Filters(过滤器)的更多相关文章

  1. Liferay7 BPM门户开发之36: 使用Portlet filters过滤器做切面AOP

    使用Portlet filters过滤器做切面AOP Portlet Filters定义于JSR286 Java Portlet Specification 2.0 Portlet Filters是为 ...

  2. Asp.Net MVC 3【Filters(过滤器)】

    这里分享MVC里的Filters(过滤器),什么是MVC里的过滤器,他的作用是什么? 过滤器的请求处理管道中注入额外的逻辑.他们提供了一个简单而优雅的方式来实现横切关注点.这个术语是指所有对应用程序的 ...

  3. vue filters过滤器

    vue filters过滤器 vue.js允许我们自定义过滤器,可被使用于一些常见的文本格式化,过滤器可以用在两个地方,双花括号插值和 v-bind表达式.最常见的就是双花括号插值. 比如如下代码:{ ...

  4. Vue.js Cookbook: 添加实例属性; 👍 axios(4万➕✨)访问API; filters过滤器;

    add instance properties //加上$,防止和已经定义的data,method, computed的名字重复,导致被覆写.//可以自定义添加其他符号. Vue.prototype. ...

  5. .net core MVC 通过 Filters 过滤器拦截请求及响应内容

    前提: 需要nuget   Microsoft.Extensions.Logging.Log4Net.AspNetCore   2.2.6: Swashbuckle.AspNetCore 我暂时用的是 ...

  6. JSP中过滤器的设置

    JSP中过滤器的设置 package com.filter; import java.io.IOException; import java.net.URLDecoder; import java.u ...

  7. JSP使用过滤器防止SQL注入

    什么是SQL注入攻击?引用百度百科的解释: sql注入_百度百科: 所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.具 ...

  8. mvc Filters 过滤器

    项目需要控制controller和action的访问权限. 看了下资料,发觉还是很方便的. 首先在mvc项目下创建一个文件夹 Filters, 然后在Filters中创建一个类.代码如下 namesp ...

  9. servlet和jsp页面过滤器Filter的作用及配置

    刚刚有个朋友问我,Servlet的过滤器有什么作用? 现在发个帖子说明一下,            过滤器是一个对象,可以传输请求或修改响应.它可以在请求到达Servlet/JSP之前对其进行预处理, ...

随机推荐

  1. IIS7配置Gzip压缩

    II7中自带了gzip功能,理论上应该比ii6配置起来应该简单一点,但是容易出的问题比较多.有的II7配置web服务器角色的时候可能没有安装启用动态内容压缩,所以这个钩子是灰色的,需要再次安装. 如图 ...

  2. Leetcode#169. Majority Element(求众数)

    题目描述 给定一个大小为 n 的数组,找到其中的众数.众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的,并且给定的数组总是存在众数. 示例 1: 输入: [3,2,3] ...

  3. Maven 分模块,启动父工程时异常

    1.1 运行方式 Maven方式:命令的 方式1:运行父工程.父工程将各个子模块聚合到一起.将ssh-web打war包发布到tomcat 方式2:直接运行web工程 其他方式:传统的,   部署到to ...

  4. 请求神器 postman安装

    1. 先下载postman(http://pan.baidu.com/s/1pLERz5p 密码:aqy2) 2.将你的包存放在文件夹中 列如名称为postman 3.在Chrome的地址栏中输入:c ...

  5. 虚拟机克隆后无法上网的解决(Centos7为例)

    说明:我的虚拟机之前配置的为静态ip 解决步骤: (1)更换mac地址 (2)删除 etc/udev/rules.d/70-persistent-net.rules 删除后重启机器,系统会自动生成一个 ...

  6. linux系统 之 git

    1,git是啥? 最流行的分布式版本控制系统,在高度易用的同时,仍然保留着初期设定的目标.它的速度飞快,极其适合管理大项目,它还有着令人难以置信的非线性分支管理系统,可以应付各种复杂的项目开发需求. ...

  7. python初级实战-----关于邮件发送问题

    python发邮件需要掌握两个模块的用法,smtplib和email,这俩模块是python自带的,只需import即可使用.smtplib模块主要负责发送邮件,email模块主要负责构造邮件. sm ...

  8. RabbitMQ简单应用の简单队列

    (1)首先创建一个maven项目: pom.xml,重点是配置RabbitMQ <dependencies> <dependency> <groupId>junit ...

  9. git-bisect last updated in 2.19.1【转】

    转自:https://git-scm.com/docs/git-bisect NAME git-bisect - Use binary search to find the commit that i ...

  10. Matlab调用C程序

    Matlab调用C程序   复制来自https://blog.csdn.net/u010839382/article/details/42463237 Matlab是矩阵语言,如果运算可以用矩阵实现, ...