【JavaWeb学习】过滤器Filter

摘要:
ServletAPI中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。如下面的filter注册顺序,FilterDemo2和FilterDemo1内容基本一致。destroy():在Web容器卸载Filter对象之前被调用。该方法在Filter的生命周期中仅执行一次。

一、简介

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

Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,如下所示:

【JavaWeb学习】过滤器Filter第1张

Filter是如何实现拦截的?

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

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

二、Filter开发入门

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

1 public class FilterDemo1 implementsFilter{
2 @Override
3     public void init(FilterConfig arg0) throwsServletException {    
4         System.out.println("Filter Init...");
5 }
6 @Override
7     public voiddoFilter(ServletRequest req, ServletResponse resp,
8             FilterChain chain) throwsIOException, ServletException {
9         //调用目标资源前
10         System.out.println("doFiliter之前 ...");
11         //调用目标资源
12 chain.doFilter(req, resp);
13         //调用目标资源后
14         System.out.println("doFiliter之后 ...");
15 }
16 @Override
17     public voiddestroy() {
18         System.out.println("Filter destroy...");
19 }
20 }

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

<filter>
    <filter-name>FilterDemo1</filter-name>
    <filter-class>cn.qust.web.filter.demo.FilterDemo1</filter-class>
</filter>
<filter-mapping>
    <filter-name>FilterDemo1</filter-name>
    <url-pattern>/index.jsp</url-pattern>
</filter-mapping>

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

如下面的filter注册顺序,FilterDemo2和FilterDemo1内容基本一致。

1 <filter>
2     <filter-name>FilterDemo1</filter-name>
3     <filter-class>cn.qust.web.filter.demo.FilterDemo1</filter-class>
4 </filter>
5 <filter-mapping>
6     <filter-name>FilterDemo1</filter-name>
7     <url-pattern>/index.jsp</url-pattern>
8 </filter-mapping>
9 <filter>
10     <filter-name>FilterDemo2</filter-name>
11     <filter-class>cn.qust.web.filter.demo.FilterDemo2</filter-class>
12 </filter>
13 <filter-mapping>
14     <filter-name>FilterDemo2</filter-name>
15     <url-pattern>/*</url-pattern>
16 </filter-mapping>

当访问index.jsp页面的时候,结果如下

【JavaWeb学习】过滤器Filter第2张

三、Filter的生命周期

init(FilterConfig filterConfig) throws ServletException:

和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(注:filter对象只会创建一次,init方法也只会执行一次。示例 )

开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

destroy()

在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

四、FilterConfig

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

  • String getFilterName():得到filter的名称。
  • String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
  • Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
  • public ServletContext getServletContext():返回Servlet上下文对象的引用。
1 public class FilterDemo2 implementsFilter{
2 @Override
3     public void init(FilterConfig config) throwsServletException {
4         String filterName =config.getFilterName();
5         System.out.println(filterName + " Init params : ");
6         //获取初始配置参数
7         Enumeration<String> initParameterNames =config.getInitParameterNames();
8         while(initParameterNames.hasMoreElements()){
9             String name =initParameterNames.nextElement();
10             String value =config.getInitParameter(name);
11             System.out.println(name + ":" +value);
12 }
13 }
14     
15 @Override
16     public voiddoFilter(ServletRequest req, ServletResponse resp,
17             FilterChain chain) throwsIOException, ServletException {
18         System.out.println("FilterDemo2 doFiliter2之前 ...");
19 chain.doFilter(req, resp);
20         System.out.println("FilterDemo2 doFiliter2之后 ...");
21 }
22 @Override
23     public voiddestroy() {
24         System.out.println("FilterDemo2 destroy...");
25 }
26 
27 }

在web.xml注册filter的时候配置好参数:

<filter>
    <filter-name>FilterDemo2</filter-name>
    <filter-class>cn.qust.web.filter.demo.FilterDemo2</filter-class>
    <init-param>
        <param-name>description</param-name>
        <param-value>filter过滤器的初始配置参数</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>FilterDemo2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
    

在tomcat的启动信息里可以看到打印结果:

【JavaWeb学习】过滤器Filter第3张

五、Filter的常见应用

1、同一全站字符编码

思路:通过配置参数encoding指明使用何种字符编码,以处理Html Form请求参数的中文问题

1)在Filter实现中对request和response进行编码设置

1 public class CharacterEncodingFilter implementsFilter{
2 
3     private FilterConfig config = null;
4     private String defaultCharset = "UTF-8";
5     
6 @Override
7     public voiddoFilter(ServletRequest request, ServletResponse response,
8             FilterChain chain) throwsIOException, ServletException {
9         //获取配置信息里的字符编码charset
10         String charset = this.config.getInitParameter("charset");
11         //如果没有配置字符编码,那么使用默认字符编码
12         if(charset==null){
13             charset = this.defaultCharset;
14 }
15         
16        17         18         //设置编码    
19 request.setCharacterEncoding(charset);
20 response.setCharacterEncoding(charset);
21         response.setContentType("text/html;charset="+charset);
22         
23 chain.doFilter(request, response);
24 }
25 
26 @Override
27     public void init(FilterConfig config) throwsServletException {
28         this.config =config;
29 }
30 @Override
31     public voiddestroy() {
32 }
33 
34 }

2)web.xml注册Filter,并配置参数charset

1 <!--同一全站字符编码过滤器1 --> 
2 <filter>
3     <filter-name>CharacterEncodingFilter</filter-name>
4     <filter-class>cn.qust.web.filter.CharacterEncodingFilter</filter-class>
5     <init-param>
6         <param-name>charset</param-name>
7         <param-value>UTF-8</param-value>
8     </init-param>
9 </filter>
10 <filter-mapping>
11     <filter-name>CharacterEncodingFilter</filter-name>
12     <url-pattern>/*</url-pattern>
13 </filter-mapping>

测试

<%@ page language="java"contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"content="text/html; charset=UTF-8">
<title>表单不同的请求方式</title>
</head>
<body>
    <form action="servlet/ServletDemo1"method="get">
        国家:<input type="text"name="country"><br/>
        <input type="submit"value="GET提交">
    </form>
    <hr/>
    <form action="servlet/ServletDemo1"method="post">
        国家:<input type="text"name="country"><br/>
        <input type="submit"value="POST提交">
    </form>
</body>
</html>
//测试统一全站字符编码
public class ServletDemo1 extendsHttpServlet {
    public voiddoGet(HttpServletRequest request, HttpServletResponse response)
            throwsServletException, IOException {
        String country = request.getParameter("country");
        response.getWriter().write("国家:" +country);
    }
    public voiddoPost(HttpServletRequest request, HttpServletResponse response)
            throwsServletException, IOException {
        doGet(request, response);
    }
}

但是get请求时会乱码,post请求则正常。

改进后的编码过滤器:

1 packagecn.qust.web.filter;
2 
3 importjava.io.IOException;
4 importjava.io.UnsupportedEncodingException;
5 
6 importjavax.servlet.Filter;
7 importjavax.servlet.FilterChain;
8 importjavax.servlet.FilterConfig;
9 importjavax.servlet.ServletException;
10 importjavax.servlet.ServletRequest;
11 importjavax.servlet.ServletResponse;
12 importjavax.servlet.http.HttpServletRequest;
13 importjavax.servlet.http.HttpServletRequestWrapper;
14 importjavax.servlet.http.HttpServletResponse;
15 
16 /*真正解决全站乱码问题
17 * 
18 * 当某个对象的方法不适应业务需求时,通常有2种方式可以对方法进行增强:
19 .编写子类,覆盖需增强的方法
20 .使用Decorator设计模式对方法进行增强(包装模式)
21 * 疑问:在实际应用中遇到需增强对象的方法时,到底选用哪种方式呢?
22 没有具体的定式,不过有一种情况下,必须使用Decorator设计模式:即被增强的对象,开发人员只能得到它的对象,无法得到它的class文件。
23 比如request、response对象,开发人员之所以在servlet中能通过sun公司定义的HttpServletRequest
esponse接口去操作这些对象,是因为Tomcat服务器厂商编写了request、response接口的实现类。web服务器在调用servlet时,会用这些接口的实现类创建出对象,然后传递给servlet程序。
24 此种情况下,由于开发人员根本不知道服务器厂商编写的request、response接口的实现类是哪个?在程序中只能拿到服务器厂商提供的对象,因此就只能采用Decorator设计模式对这些对象进行增强。
25 
26  */
27 
28 /*
29 * Filter高级应用:Decorator设计模式
30 * 涉及到的代码文件:CharacterEncodingFilter2.java    text.jsp    ServletDemo2.java
31 * 测试:在text.jsp中的textArea中输入文字,提交之后有ServletDemo2正常显示出来
32  */
33 public class CharacterEncodingFilter2 implementsFilter {
34     private FilterConfig config = null;
35     private String defaultCharset = "UTF-8";
36     
37 @Override
38     public voiddoFilter(ServletRequest request, ServletResponse response,
39             FilterChain chain) throwsIOException, ServletException {
40         
41         //获取配置信息里的字符编码charset
42         String charset = this.config.getInitParameter("charset");
43         //如果没有配置字符编码,那么使用默认字符编码
44         if(charset==null){
45             charset = this.defaultCharset;
46 }
47         //设置编码    
48 request.setCharacterEncoding(charset);
49 response.setCharacterEncoding(charset);
50         response.setContentType("text/html;charset="+charset);
51         
52         chain.doFilter(newMyRequest((HttpServletRequest) request), response);
53 }
54 
55 @Override
56     public void init(FilterConfig filterConfig) throwsServletException {
57         this.config =filterConfig;
58 }
59 @Override
60     public voiddestroy() {
61 }
62     //request的内部包装类
63     class MyRequest extendsHttpServletRequestWrapper{
64         private HttpServletRequest request = null;
65         publicMyRequest(HttpServletRequest request) {
66             super(request);
67             this.request =request;
68 }
69         //要增强的方法
70 @Override
71         publicString getParameter(String name) {
72             String value =request.getParameter(name);
73             String method =request.getMethod();
74             if(value!=null && method.equalsIgnoreCase("GET")){
75                 try{
76                     value = new String(value.getBytes("iso-8859-1"),request.getCharacterEncoding());
77                 } catch(UnsupportedEncodingException e) {
78 e.printStackTrace();
79 }
80 }
81             returnvalue;
82 }
83 }
84 }

2、禁止浏览器缓存所有动态页面

有 3 个 HTTP 响应头字段都可以禁止浏览器缓存当前页面,它们在 Servlet 中的示例代码如下:

  • response.setDateHeader("Expires",-1);
  • response.setHeader("Cache-Control","no-cache");?
  • response.setHeader("Pragma","no-cache");?

并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。

Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面

Cache-Control响应头有两个常用值:

no-cache指浏览器不要缓存当前页面。

max-age:xxx指浏览器缓存页面xxx秒

1 packagecn.qust.web.filter;
2 
3 importjava.io.IOException;
4 
5 importjavax.servlet.Filter;
6 importjavax.servlet.FilterChain;
7 importjavax.servlet.FilterConfig;
8 importjavax.servlet.ServletException;
9 importjavax.servlet.ServletRequest;
10 importjavax.servlet.ServletResponse;
11 importjavax.servlet.http.HttpServletRequest;
12 importjavax.servlet.http.HttpServletResponse;
13 
14 
15 /*
16 * Filter应用2:禁止浏览器缓存页面
17 * 涉及到的代码文件:NoCacheFilter.java   index.jsp
18 * 测试:在浏览器缓存文件夹中查看。
19 * 实验结果:失败。缓存文件夹中依然存在着缓存文件
20  */
21 public class NoCacheFilter implementsFilter{
22 
23 @Override
24     public voiddestroy() {
25 }
26 
27 @Override
28     public voiddoFilter(ServletRequest req, ServletResponse resp,
29             FilterChain chain) throwsIOException, ServletException {
30         
31         HttpServletRequest request =(HttpServletRequest) req;
32         HttpServletResponse response =(HttpServletResponse) resp;
33         
34         response.setDateHeader("Expires",-1);
35         response.setHeader("Cache-Control","no-cache");
36         response.setHeader("Pragma","no-cache");
37         
38 chain.doFilter(request, response);
39 }
40 
41 @Override
42     public void init(FilterConfig arg0) throwsServletException {
43         //TODO Auto-generated method stub
44 }
45 }

3、控制浏览器缓存页面中的静态资源

场景:有些动态页面中引用了一些图片或css文件以修饰页面效果,这些图片和css文件经常是不变化的,所以为减轻服务器的压力,可以使用filter控制浏览器缓存这些文件,以提升服务器的性能。

1 packagecn.qust.web.filter;
2 
3 importjava.io.IOException;
4 importjava.text.SimpleDateFormat;
5 importjava.util.Date;
6 importjava.util.Enumeration;
7 importjava.util.HashMap;
8 importjava.util.Map;
9 importjava.util.TimeZone;
10 
11 importjavax.servlet.Filter;
12 importjavax.servlet.FilterChain;
13 importjavax.servlet.FilterConfig;
14 importjavax.servlet.ServletException;
15 importjavax.servlet.ServletRequest;
16 importjavax.servlet.ServletResponse;
17 importjavax.servlet.http.HttpServletRequest;
18 importjavax.servlet.http.HttpServletResponse;
19 
20 
21 /*
22 * Filter应用3:控制浏览器缓存页面中的静态资源的过滤器
23 * 涉及的代码文件:CacheFilter.java    image.jsp
24 * 测试:打开image.jsp页面,查看缓存文件夹中的缓存
25  */
26 public class CacheFilter implementsFilter {
27     private Map<String, Integer> expires = null;
28     private FilterConfig config = null;
29 @Override
30     public voiddestroy() {
31 }
32 
33 @Override
34     public voiddoFilter(ServletRequest req, ServletResponse resp,
35             FilterChain chain) throwsIOException, ServletException {
36         HttpServletRequest request =(HttpServletRequest) req;
37         HttpServletResponse response =(HttpServletResponse) resp;
38         //获取请求地址的uri
39         String uri =request.getRequestURI();
40         //获取文件后缀
41         String fileType = uri.substring(uri.lastIndexOf(".")+1);
42         //缓存时间
43         long expires = -1;
44         if(this.expires.containsKey(fileType)){
45             expires = System.currentTimeMillis() + this.expires.get(fileType) ;
46             //时间差8个小时
47             expires += 8*60*60*1000;
48 }
49         //设置缓存时间
50         response.setDateHeader("Expires", expires);    
51         System.out.println("Expires:" +expires);
52         //调用目标资源
53 chain.doFilter(request, response);
54 }
55 
56 @Override
57     public void init(FilterConfig arg0) throwsServletException {
58         this.config =arg0;
59         //保存缓存文件类型及对应的缓存时间
60         this.expires = new HashMap<String, Integer>();
61         //把配置信息存放到map中
62         Enumeration<String> names = this.config.getInitParameterNames();
63         while(names.hasMoreElements()){
64             String fileType =names.nextElement();
65             Integer expireTime = Integer.valueOf(this.config.getInitParameter(fileType));//单位为分钟
66             expireTime = expireTime * 60 * 1000;
67             this.expires.put(fileType, expireTime);//单位为秒
68 }
69         System.out.println(this.expires.toString());
70 }
71 }

4、实现URL级别的权限认证

5、实现用户自动登录

【JavaWeb学习】过滤器Filter第4张

免责声明:文章转载自《【JavaWeb学习】过滤器Filter》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇输入框获取焦点时value值隐藏与失去焦点时显示FPGA使用技巧下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

10 深入kubernete落地实践深入101 ingress四层代理、session保持、定制配置、流量控制

深入Ingress-Nginx Deployment?   https 四层代理    访问控制 定制配置 项目 8-ingress 修改部署ds方式 Deployment 改成DaemonSet(ds)模式 #保存controller到文件 kubectl get deploy -n ingress-nginx nginx-ingress-control...

Asp.net Web.Config 配置元素customErrors

Asp.net配置文件的配置方式,其实在MSDN里面是写得最清楚的了。可惜之前一直未曾了解到MSDN的强大。 先贴个地址:http://msdn.microsoft.com/zh-cn/library/dayb112d(v=vs.80).aspx,然后逐个分析。我希望自己能够从头到尾认真学完这系列东西。为了不至于让自己太早放弃,我决定从自己用过的配置文件学...

Jsp基础知识

Jsp:Java Server Pages http://127.0.0.1:8080/weba/hello.html Tomcat执行过程: 浏览器通过http协议发送请求,以TCP协议作为底层,去tomcat的安装目录下找到webapps下的weba文件夹,再继续找到hello.html. http协议有协议头和协议头,底层是TCP,是无状态的,两次连...

Springboot 解析 json 并返回+ Jsoup介绍及解析常用方法

1、配置pom.xml <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.11.2...

c++与C# winform的消息通讯--(结构体与byte数组的使用)

转载:http://www.cnblogs.com/lizhaoduo/p/3870809.html 近期正在做一个蓝牙驱动的使用程序,其中有一块从c++发送数据到C#的部分,网上查了很多资料,大多都是介绍如何通过调用函数获取用户数据。并且在消息发送中,很少介绍如何发送一个结构体,并且结构体里面有 byte数组(硬件开发常用)等如何进行处理。 首先c++里...

图片保存到数据库以及C#读取图片

图片保存到数据库,如果是sqlserver就是Image类型,如果保存到Oracle就是blob类型,在c#中相对应的就是byte[]类型,同时只需要对读出的数据强制转换就行(byte[])object. 1. 将图片保存为byte数组     //参数是图片路径,返回Byte[]类型     public byte[] GetPictureData(s...