Servlet过滤器

Servlet过滤器是一种小型的、可插入的Web组件,其提供了对Web应用程序的前期处理和后期处理的逻辑控制,可以拦截请求和响应,以便查看、提取或以某种方式操作正在客户端和服务器之间进行交换的数据。Servlet过滤器具有动态、灵活、透明、模块化、可移植、可重用等性质,是一种自包含的,模块化的组件,可以在使用时将其添加到请求/响应链中,也可以被删除而不影响到应用程序的其他Web组件。Servlet过滤器应用非常广泛,有拦截的地方一般都可以用到过滤器,其也和Servlet类似,也需要在Web应用配置文件(即Web.xml)中进行配置部署。

过滤器并不是servlet,他们并不实际创建一个请求。他们是请求到达一个servlet前的预处理程序,和/或响应离开servlet后的后处理程序。一个过滤器能够:
1.在一个servlet被调用前截获该调用
2.在一个servlet被调用前检查请求
3.修改在实际请求中提供了可定制请求对象的请求头和请求数据
4.修改在实际响应中提供了可定制响应对象的响应头和响应数据

过滤器类必须要实现Filter接口,这个接口在javax.servlet包中。该接口有三个方法如下:

1.void init(FilterConfig config) throws ServletException:在过滤器执行service前被调用,以设置过滤器的配置对象。服务器调用一次init(FilterConfig)以为服务准备过滤器,然后在请求需要使用过滤器的任何时候调用doFilter()。FilterConfig接口检索过滤器名、初始化参数以及活动的servlet上下文。
2.void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) throws IOException,ServletException;执行实际的过滤工作。在doFilter()方法中,每个过滤器都接受当前的请求和响应,而FilterChain包含的过滤器则仍然必须被处理。doFilter()方法中,过滤器可以对请求和响应做它想做的一切。过滤器调用chain.doFilter()将控制权传送给下一个过滤器。当这个调用返回后,过滤器可以在它的doFilter()方法的最后对响应做些其他的工作;例如,它能记录响应的信息。如果过滤器想要终止请求的处理或或得对响应的完全控制,则他可以不调用下一个过滤器。
3.void destroy();在过滤器执行service后被调用。服务器调用destory()以指出过滤器已结束服务。

下面是一个很经典的例子,记录请求所持续的时间。

  1. import java.io.*;
  2. import javax.servlet.*;
  3. import javax.servlet.http.*;
  4. public class TimerFilter implements Filter
  5. {
  6. private FilterConfig config = null;
  7. //初始化
  8. public void init(FilterConfig config) throws ServletException{
  9. this.config = config;
  10. }
  11. public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
  12. throws IOException, ServletException{
  13. long before = System.currentTimeMillis(); //请求处理前获取系统时间
  14. chain.doFilter(request, response); //将请求转发到过滤器链上的其他对象
  15. long after = System.currentTimeMillis(); //请求处理后获取系统时间
  16. String name = "";
  17. if (request instanceof HttpServletRequest){
  18. name = ((HttpServletRequest)request).getRequestURI(); //获取发出请求的URI
  19. }
  20. config.getServletContext().log(name + ": " + (after - before) + "ms"); //记录信息
  21. }
  22. //结束服务
  23. public void destroy(){
  24. config = null;
  25. }
  26. }

前面说过,过滤器要在Web.xml文件中部署,部署跟Servlet部署非常相似,就是把Servlet部署中的标签中servlet改为filter就可以了。
web.xml

  1. <filter>
  2. <filter-name>timerFilter</filter-name>
  3. <filter-class>TimerFilter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>timerFilter</filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </filter-mapping>


Servlet过滤器简介

  • Servlet过滤器是通过一个配置文件来灵活声明的模块化的可重用组建。Servlet过滤器截请求和响应,以便查看、提取或操作客服端和服务器之间交换的数据。
  • Servlet过滤器实际上就是一个标准的java类,这个类通过实现Filter接口获得过滤器的功能。它在jsp容器启动的时候通过web.xml配置文件被系统加载。
  • Servlet过滤器在接收到用户请求的时候被调用,当服务器接收到用户的请求的时候,依次调用配置好的过滤器,完成后将执行请求所要求的servlet,而servlet执行后的响应,则先通过配置好的过滤器后再发送给用户。

过滤器的用途:

  • 1、 用户认证和授权管理。
  • 2、 统计web应用的访问量和访问命中率,生成访问报告。
  • 3、 实现web应用的日志处理功能。
  • 4、 实现数据压缩功能。
  • 5、 对传输的数据进行加密。
  • 6、 实现xml文件的XSLT的转换。
    1. 一个servlet过滤器其实是一个java类,它的实现需要分为两个部分,java类自身以及在web.xml文件中的XML描述。对于filter接口,该接口由一对描述的生命周期的方法init(),destroy(),init方法在服务器初始化过滤器的时候会调用,而destory方法在服务器关闭的时候会调用,还有一个行为方法doFilter方法会在执行过滤操作的时候调用.

Servlet过滤器的配置

  1. Servet过滤器需要通过web应用程序部署描述符文件web.xml来部署到应用中。<br /> 配置如下<br /> <br />**[html]** [view plain](http://blog.csdn.net/chenssy/article/details/6752972#) [copy](http://blog.csdn.net/chenssy/article/details/6752972#)
  1. < filter>
  2. <filter-name>Filtername</filter-name>
  3. <filter-class>com.filter.Filter/class</filter-class>
  4. <init-param>
  5. <param-name>file</param-name>
  6. <param-value>filename</param-value>
  7. </init-param>
  8. </filter>
  9. <filter-mapping>
  10. <filter-name>Filtername</filter-name>
  11. <url-pattern>/*</url-pattern>
  12. </filter-mapping>

下面是示例:
使用过滤器解决中文编码问题:
由于java的默认编码方式是ISO-8859-1,而通常编写中文应用程序的时候都是使用GB2312或gbk编码方式。在这种情况下,应在页面的首部通过``<%@ page contentType="text/html;charset=gbk"%>命令来指定页面的编码方式。这样中文页面就可以正常地显示了。但是如果页面中村中表单。如一个input输入框,如果访问者在其中输入中文,又提交到某个servlet进行处理的话,java会首先按ISO-5589-1的默认方式对这段文本进行编码,然后交给servet处理,处理后的文本将还是以ISO-5589-1编码方式村中,如果这个时候这个文本返回一个按GBK编码来显示的页面,由于编码格式的不同,很显然得不到正确的显示结果。
对于编码方式的解决方法有很多种,这里主要介绍用过滤器来解决中文编码问题:
[java]

  1. package com.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. public class CharacterEncodingFilter implements Filter {
  10. private FilterConfig config;
  11. private String encoding = "ISO8859_1";
  12. public void destroy() {
  13. config = null;
  14. }
  15. public void doFilter(ServletRequest request, ServletResponse response ,
  16. FilterChain chain) throws IOException, ServletException {
  17. request.setCharacterEncoding(encoding);
  18. chain.doFilter(request, response);
  19. }
  20. public void init(FilterConfig config) throws ServletException {
  21. this.config = config;
  22. String s = config.getInitParameter("encoding");
  23. if(s!=null){
  24. encoding = s;
  25. }
  26. }
  27. }

然后在是XML的配置:
[html] view plain copy

  1. <filter>
  2. <filter-name>encodingFilter</filter-name>
  3. <filter-class>com.filter.CharacterEncodingFilter</filter-class>
  4. <init-param>
  5. <param-name>encoding</param-name>
  6. <param-value>utf-8</param-value>
  7. </init-param>
  8. </filter>
  9. <filter-mapping>
  10. <filter-name>encodingFilter</filter-name>
  11. <url-pattern>/*</url-pattern>
  12. </filter-mapping>

最后是encoding。jsp的编写?:
[html] view plain copy

  1. 用户名:<c:out value="${param.username}" default="none"></c:out><br>
  2. 密码:<c:out value="${param.userpassword}" default="none"></c:out><br>
  3. <form action="MyJsp.jsp" method="post">
  4. 用户名:<input type="test" name="username"> <br>
  5. 密码:<input type="password" name="userpassword"><br>
  6. <input type="submit" value="提交">
  7. </form>

启动tomcat,访问encoding.jsp,输入“张山”就可以看到,经过过滤器后,页面可以正常显示服务器传出的信息。。。

使用过滤器记录用户访问日志
对于有些项目,它对于用户的每次访问都要有详细的记录。那么这是使用记录日志是一个非常好的解决方法,使用过滤器就可以很轻松地对每次用户的访问进行记录。但是由于同一个访问者在同一个时段访问站点不同的页面时,不能重复记录日志,否则日志将会在很短的时间内塞满服务器的硬盘空间。于是这里可以利用session对象来判断用户的每次会话,在一次会话中,过滤器只会记录一次。
下面编写LogFilter类,这个过滤器主要负责记录用户的访问记录:
[html] view plain copy

  1. package com.filter;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.RandomAccessFile;
  5. import java.text.SimpleDateFormat;
  6. import java.util.Date;
  7. import javax.servlet.Filter;
  8. import javax.servlet.FilterChain;
  9. import javax.servlet.FilterConfig;
  10. import javax.servlet.ServletException;
  11. import javax.servlet.ServletRequest;
  12. import javax.servlet.ServletResponse;
  13. import javax.servlet.http.HttpServletRequest;
  14. import javax.servlet.http.HttpSession;
  15. import com.sun.org.apache.bcel.internal.generic.NEW;
  16. public class LoginFilter implements Filter{
  17. private FilterConfig config =null;
  18. private String filename = null;
  19. private String filtername = null;
  20. public void destroy() {
  21. this.config = null;
  22. this.filename = null;
  23. this.filtername = null;
  24. }
  25. public void doFilter(ServletRequest request , ServletResponse response,
  26. FilterChain chain ) throws IOException, ServletException {
  27. HttpServletRequest hRequest = (HttpServletRequest) request;
  28. // 获取session对象
  29. HttpSession session = hRequest.getSession();
  30. // 先判断session中的LOGGED是否有值,如没有则说明是新的请求
  31. if(null==session.getAttribute("LOGGED")){
  32. session.setAttribute("LOGGED", "yes"); // 设置LOGGED的值为yes,防止同一会话重复记录
  33. File file = new File(this.filename);
  34. if(!file.exists())
  35. file.createNewFile(); // 判断文件是否存在,如果不存在,就建立一个新的
  36. /*
  37. * 创建日志记录内容logContent包括访问者的IP, 访问的页面URL和访问的时间以及日志过滤器的名字
  38. */
  39. String logContent = hRequest.getRemoteHost()+"->"+hRequest.getRequestURI()+" Logged "+getTime()+" By s"+this.filtername+"\r\n";
  40. RandomAccessFile rf = new RandomAccessFile(this.filename,"rw"); // 建立一个随机文件操作对象
  41. rf.seek(rf.length()); // 将写入指针指向文件的尾部,rf.length()获得文件的长度,seek文件长度这么长得距离正好是文件的尾部
  42. rf.writeBytes(logContent); // 将日志写入到文件中去
  43. rf.close(); // 关闭文件
  44. }
  45. chain.doFilter(request, response);
  46. }
  47. public void init(FilterConfig config) throws ServletException {
  48. this.config = config;
  49. this.filename = this.config.getInitParameter("file");
  50. this.filtername = this.config.getFilterName();
  51. }
  52. // 获取时间
  53. private String getTime(){
  54. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
  55. return sdf.format(new Date());
  56. }
  57. }

这里利用session来限制同一个会话只会记录一次日志,而不管这个会话中的访问者访问了多少个页面,在第一次访问的时候,由于session的LOGGED是空的,所以这个时候记录日志并把LOGGED设置为yes,这样第二次判断的时候将不会执行记录日志。
然后在来配置XML:
[html] view plain copy

  1. <filter>
  2. <filter-name>LogFilter</filter-name>
  3. <filter-class>com.filter.LoginFilter</filter-class>
  4. <init-param>
  5. <param-name>file</param-name>
  6. <param-value>D:/log.txt</param-value>
  7. </init-param>
  8. </filter>
  9. <filter-mapping>
  10. <filter-name>LogFilter</filter-name>
  11. <url-pattern>/*</url-pattern>
  12. </filter-mapping>

配置好XML后,访问跟目录下地任何文件,都会可以在D:/log.txt文件中得到访问者的记录: