zoukankan      html  css  js  c++  java
  • 【JavaWeb学习】过滤器Filter

    一、简介

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

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

      

    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 implements Filter{
     2     @Override
     3     public void init(FilterConfig arg0) throws ServletException {    
     4         System.out.println("Filter Init...");
     5     }
     6     @Override
     7     public void doFilter(ServletRequest req, ServletResponse resp,
     8             FilterChain chain) throws IOException, ServletException {
     9         //调用目标资源前
    10         System.out.println("doFiliter之前 ...");
    11         //调用目标资源
    12         chain.doFilter(req, resp);
    13         //调用目标资源后
    14         System.out.println("doFiliter之后 ...");
    15     }
    16     @Override
    17     public void destroy() {
    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页面的时候,结果如下

    三、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 implements Filter{
     2     @Override
     3     public void init(FilterConfig config) throws ServletException {
     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 void doFilter(ServletRequest req, ServletResponse resp,
    17             FilterChain chain) throws IOException, ServletException {
    18         System.out.println("FilterDemo2 doFiliter2之前 ...");
    19         chain.doFilter(req, resp);
    20         System.out.println("FilterDemo2 doFiliter2之后 ...");
    21     }
    22     @Override
    23     public void destroy() {
    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的启动信息里可以看到打印结果:

    五、Filter的常见应用

    1、同一全站字符编码

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

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

     1 public class CharacterEncodingFilter implements Filter{
     2 
     3     private FilterConfig config = null;
     4     private String defaultCharset = "UTF-8";
     5     
     6     @Override
     7     public void doFilter(ServletRequest request, ServletResponse response,
     8             FilterChain chain) throws IOException, 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) throws ServletException {
    28         this.config = config;
    29     }
    30     @Override
    31     public void destroy() {
    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 extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String country = request.getParameter("country");
            response.getWriter().write("国家:" + country);
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    }

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

    改进后的编码过滤器:

     1 package cn.qust.web.filter;
     2 
     3 import java.io.IOException;
     4 import java.io.UnsupportedEncodingException;
     5 
     6 import javax.servlet.Filter;
     7 import javax.servlet.FilterChain;
     8 import javax.servlet.FilterConfig;
     9 import javax.servlet.ServletException;
    10 import javax.servlet.ServletRequest;
    11 import javax.servlet.ServletResponse;
    12 import javax.servlet.http.HttpServletRequest;
    13 import javax.servlet.http.HttpServletRequestWrapper;
    14 import javax.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 implements Filter {
    34     private FilterConfig config = null;
    35     private String defaultCharset = "UTF-8";
    36     
    37     @Override
    38     public void doFilter(ServletRequest request, ServletResponse response,
    39             FilterChain chain) throws IOException, 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(new MyRequest((HttpServletRequest) request), response);
    53     }
    54 
    55     @Override
    56     public void init(FilterConfig filterConfig) throws ServletException {
    57         this.config = filterConfig;
    58     }
    59     @Override
    60     public void destroy() {
    61     }
    62     //request的内部包装类
    63     class MyRequest extends HttpServletRequestWrapper{
    64         private HttpServletRequest request = null;
    65         public MyRequest(HttpServletRequest request) {
    66             super(request);
    67             this.request = request;
    68         }
    69         //要增强的方法
    70         @Override
    71         public String 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             return value;
    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 package cn.qust.web.filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 import javax.servlet.http.HttpServletRequest;
    12 import javax.servlet.http.HttpServletResponse;
    13 
    14 
    15 /*
    16  * Filter应用2:禁止浏览器缓存页面
    17  * 涉及到的代码文件:NoCacheFilter.java   index.jsp
    18  * 测试:在浏览器缓存文件夹中查看。
    19  * 实验结果:失败。缓存文件夹中依然存在着缓存文件
    20  */
    21 public class NoCacheFilter implements Filter{
    22 
    23     @Override
    24     public void destroy() {
    25     }
    26 
    27     @Override
    28     public void doFilter(ServletRequest req, ServletResponse resp,
    29             FilterChain chain) throws IOException, 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) throws ServletException {
    43         // TODO Auto-generated method stub
    44     }
    45 }

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

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

     1 package cn.qust.web.filter;
     2 
     3 import java.io.IOException;
     4 import java.text.SimpleDateFormat;
     5 import java.util.Date;
     6 import java.util.Enumeration;
     7 import java.util.HashMap;
     8 import java.util.Map;
     9 import java.util.TimeZone;
    10 
    11 import javax.servlet.Filter;
    12 import javax.servlet.FilterChain;
    13 import javax.servlet.FilterConfig;
    14 import javax.servlet.ServletException;
    15 import javax.servlet.ServletRequest;
    16 import javax.servlet.ServletResponse;
    17 import javax.servlet.http.HttpServletRequest;
    18 import javax.servlet.http.HttpServletResponse;
    19 
    20 
    21 /*
    22  * Filter应用3:控制浏览器缓存页面中的静态资源的过滤器
    23  * 涉及的代码文件:CacheFilter.java    image.jsp
    24  * 测试:打开image.jsp页面,查看缓存文件夹中的缓存
    25  */
    26 public class CacheFilter implements Filter {
    27     private Map<String, Integer> expires = null;
    28     private FilterConfig config = null;
    29     @Override
    30     public void destroy() {
    31     }
    32 
    33     @Override
    34     public void doFilter(ServletRequest req, ServletResponse resp,
    35             FilterChain chain) throws IOException, 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) throws ServletException {
    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、实现用户自动登录

  • 相关阅读:
    2019-8-22学习笔记---文件上传与读取
    JS字符串的操作
    JS控制台打印星星,总有你要的那一款~呐~给你小心心哦~~~❤
    DOM的操作(增删改查)
    js上传视频(jquery.form.js)
    vue单页面模板说明文档(3)
    vue单页面模板说明文档(2)
    vue单页面模板说明文档(1)
    js 通过url获取里面的参数值
    ios点击输入框,界面放大解决方案
  • 原文地址:https://www.cnblogs.com/lhat/p/6359612.html
Copyright © 2011-2022 走看看