• Servlet 过滤器


      过滤器Filter,是介于Servlet之前,可拦截过滤浏览器对Servlet的请求,也可以改变Servlet对浏览器的响应。

    一、过滤器的概念

        现在有以下几个请求:

            1、针对所有的Servlet,产品经理想要了解从请求到响应之间的时间差。

            2、针对某些特定的页面,希望仅有几个用户才能知道。

            3、基于安全方面,用户希望输入的特定字符必须过滤并且替换为无害的字符。

            4、请求与响应的编码从Big5改为UTF-8。

        思路分析:

            1、运行Servlet的service()方法前后,各记录一个时间,计算其差。:过滤器

            2、运行Servlet的service()方法前,验证是否为允许的用户。

            3、运行Servlet的service()方法前,对请求参数进行字符过滤与替换。

            4、运行Servlet的service()方法前,对请求与响应对象设置编码。

        性能评测、用户验证、字符替换、编码设置这类需求,应该设置为独立的元件,随时可以加入到应用程序中,这类元件就像过滤器,安插在浏览器与Servlet之间。如图、

            

    二、过滤器的实现与设置

        必须实现Filter接口,并用@WebFilter标注。Filter接口主要实现三个方法:init()、doFilter()、destroy()

        FilterConfig类似于Servlet接口init()方法参数上的ServletConfig,FilterConfig设置信息的代表对象。如果定义过滤器时设置了初始参数,可以通过FilterConfig的getInitParameter()方法来取得初始参数。

        Filter接口的doFilter()方法,类似Servlet接口的service()方法,当请求来到容器,而容器发现调用Servlet的service()方法前,就会调用该过滤器的doFilter()方法。可以再doFilter()方法中进行service()方法的前置处理,而后决定是否调用FilterChaindoFilter()方法。

        如果调用了FilterChain的doFilter()方法,就会执行下一个过滤器,如果没有下一个过滤器了,就调用Servlet的service()方法,如果因为某个情况没有执行下一个过滤器(如,用户没有通过验证)而没有调用FilterChaindoFilter()方法,则请求就不会交给目标Servlet。FilterChain运行后会以堆栈顺序返回。

        例1:实现简单的性能评测过滤器,记录请求和响应间的时间差,了解Servlet处理请求到响应所花的时间。

     1 package ServletAPI;
     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.annotation.WebInitParam;
    12 
    13 /**
    14  * Servlet Filter implementation class PerformanceFilter
    15  */
    16 @WebFilter(
    17         filterName = "performance", 
    18         urlPatterns = { "/*" } 
    19         )
    20 public class PerformanceFilter implements Filter {
    21     private FilterConfig fConfig;
    22     /**
    23      * Default constructor. 
    24      */
    25     public PerformanceFilter() {
    26         // TODO Auto-generated constructor stub
    27     }
    28 
    29     /**
    30      * @see Filter#destroy()
    31      */
    32     public void destroy() {
    33         // TODO Auto-generated method stub
    34     }
    35 
    36     /**
    37      * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
    38      */
    39     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    40         // TODO Auto-generated method stub
    41         // place your code here
    42 
    43         // pass the request along the filter chain
    44         long begin=System.currentTimeMillis();
    45         chain.doFilter(request, response);
    46         fConfig.getServletContext().log("Request process in "+(System.currentTimeMillis()-begin)+" millseconds.");
    47     }
    48 
    49     /**
    50      * @see Filter#init(FilterConfig)
    51      */
    52     public void init(FilterConfig fConfig) throws ServletException {
    53         // TODO Auto-generated method stub
    54         this.fConfig=fConfig;
    55     }
    56 
    57 }
    PerformanceFilter.java

    三、请求封装器

    1、实现字符替换过滤器

        假设用户不希望在留言中输入<a href=’http://openhome.cc’>openhome.cc</a>这样的信息,希望直接变成超链接。希望将一些html过滤掉,如将<、>角括号置换为HTML实体字符&lt;  &gt;

        EscapeWrapper类继承了HttpServletRequestWrapper,并定义了一个接受HttpSevletRequest的构造器,真正的HttpSevletRequest将通过此构造器传入,必须使用super()调用HttpSevletRequestWrapper接受HttpSevletRequest的构造器,之后要取得被封装的HttpSevletRequest,则可以调用getRequest()方法。

     1 package ServletAPI;
     2 import javax.servlet.http.HttpServletRequest;
     3 import javax.servlet.http.HttpServletRequestWrapper;
     4 
     5 
     6 import org.apache.commons.lang3.StringEscapeUtils;
     7 
     8 public class EscapeWrapper extends HttpServletRequestWrapper {
     9     public EscapeWrapper(HttpServletRequest request){
    10         super(request);//必须调用父类的构造器,传入HttpServletRequest的实例。
    11     }
    12     public String getParameter(String name){//重新定义getParameter方法
    13         String value=getRequest().getParameter(name);
    14         return StringEscapeUtils.escapeHtml4(value);//将取得的请求参数值进行字符替换
    15     }
    16 }
    EscapeWrapper.java

      可以使用这个请求封装器类搭配过滤器,以进行字符过滤操作的服务。例如:

     1 package ServletAPI;
     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.annotation.WebFilter;
    12 import javax.servlet.http.HttpServlet;
    13 import javax.servlet.http.HttpServletRequest;
    14 
    15 /**
    16  * Servlet Filter implementation class EscapeFilter
    17  */
    18 @WebFilter("/*")
    19 public class EscapeFilter implements Filter {
    20 
    21     /**
    22      * Default constructor. 
    23      */
    24     public EscapeFilter() {
    25         // TODO Auto-generated constructor stub
    26     }
    27 
    28     /**
    29      * @see Filter#destroy()
    30      */
    31     public void destroy() {
    32         // TODO Auto-generated method stub
    33     }
    34 
    35     /**
    36      * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
    37      */
    38     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    39         // TODO Auto-generated method stub
    40         // place your code here
    41 
    42         // pass the request along the filter chain
    43         HttpServletRequest requestWrapper=new EscapeWrapper((HttpServletRequest) request);//将原请求对象包裹至EscapeWrapper中。
    44         
    45         chain.doFilter(request, response);//将EscapeWrapper对象当做请求对象传入doFilter()
    46     }
    47 
    48     /**
    49      * @see Filter#init(FilterConfig)
    50      */
    51     public void init(FilterConfig fConfig) throws ServletException {
    52         // TODO Auto-generated method stub
    53     }
    54 
    55 }
    EscapeFilter.java

      运行结果:

        

    2、实现编码设置过滤器

        实现方式:过滤器+封装器

     1 package ServletAPI;
     2 
     3 import java.io.UnsupportedEncodingException;
     4 
     5 import javax.servlet.http.HttpServletRequest;
     6 import javax.servlet.http.HttpServletRequestWrapper;
     7 
     8 public class EncodingWrapper extends HttpServletRequestWrapper{
     9     private String ENCODING;
    10     public EncodingWrapper(HttpServletRequest request,String ENCODING){
    11         super(request);
    12         this.ENCODING=ENCODING;
    13     }
    14     public String getParameter(String name){
    15         String value=getRequest().getParameter(name);
    16         if(value!=null){
    17             try {
    18                 byte[] b=value.getBytes("ISO-8859-1");
    19                 value=new String(b,ENCODING);//编码转换
    20             } catch (UnsupportedEncodingException e) {
    21                 // TODO: handle exception
    22                 throw new RuntimeException(e);
    23             }
    24         }
    25         return value;
    26     }
    27 }
    EncodingWrapper.java
     1 package ServletAPI;
     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.annotation.WebFilter;
    12 import javax.servlet.annotation.WebInitParam;
    13 import javax.servlet.http.HttpServletRequest;
    14 
    15 import org.omg.CORBA.PUBLIC_MEMBER;
    16 
    17 /**
    18  * Servlet Filter implementation class EncodingFilter
    19  */
    20 @WebFilter(
    21         urlPatterns = { "/*" }, 
    22         initParams = { 
    23                 @WebInitParam(name = "ENCODING", value = "UTF-8")
    24         })
    25 public class EncodingFilter implements Filter {
    26     private String ENCODING;
    27     /**
    28      * Default constructor. 
    29      */
    30     public EncodingFilter() {
    31         // TODO Auto-generated constructor stub
    32     }
    33 
    34     /**
    35      * @see Filter#destroy()
    36      */
    37     public void destroy() {
    38         // TODO Auto-generated method stub
    39     }
    40 
    41     /**
    42      * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
    43      */
    44     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    45         // TODO Auto-generated method stub
    46         // place your code here
    47 
    48         // pass the request along the filter chain
    49         HttpServletRequest req=(HttpServletRequest)request;
    50         if("GET".equals(req.getMethod())){
    51             req=new EncodingWrapper(req,ENCODING);//GET请求时创建封装器
    52         }
    53         else{
    54             req.setCharacterEncoding(ENCODING);
    55         }
    56         chain.doFilter(request, response);
    57     }
    58 
    59     /**
    60      * @see Filter#init(FilterConfig)
    61      */
    62     public void init(FilterConfig fConfig) throws ServletException {
    63         // TODO Auto-generated method stub
    64         ENCODING=fConfig.getInitParameter("ENCODING");//读取初始参数
    65     }
    66 
    67 }
    EncodingFilter.java

    四、响应封装器

        Servlet是通过HttpServletResponse对象来对浏览器进行响应的,通过getWriter()取得PrintWriter,或是通过getOutputStream()取得ServletOutputStream。如果想要对响应的内容进行压缩处理,主要继承了HttpServletResponseWrapper之后,重新定义这两个方法实现的。

        用GZIPOutputStream类实现压缩,其增加了压缩输出功能,重新定义writer()方法,通过GZIPOutputStream的writer()方法来做串流输出,GZIPOutputStream的writer()实现了压缩的功能。

     1 package ServletAPI;
     2 
     3 import java.io.IOException;
     4 import java.util.zip.GZIPOutputStream;
     5 
     6 import javax.servlet.ServletOutputStream;
     7 
     8 public class GZipServletOutputStream  extends ServletOutputStream{
     9     private GZIPOutputStream gzipOutputStream;
    10     public GZipServletOutputStream(ServletOutputStream servletOutputStream) throws IOException{
    11         this.gzipOutputStream=new GZIPOutputStream(servletOutputStream);
    12         //使用GZIPOutputStream来增加压缩功能。
    13     }
    14     @Override
    15     public void write(int b) throws IOException {
    16         // TODO Auto-generated method stub
    17         gzipOutputStream.write(b);//输出时通过GZIPOutputStream的write()方法来压缩输出
    18     }
    19     public GZIPOutputStream getGzipOutputStream(){
    20         return gzipOutputStream;
    21     }
    22 
    23 }
    GZipServletOutputStream.java
     1 package ServletAPI;
     2 
     3 import java.io.IOException;
     4 import java.io.OutputStreamWriter;
     5 import java.io.PrintWriter;
     6 import java.util.zip.GZIPOutputStream;
     7 
     8 import javax.servlet.ServletOutputStream;
     9 import javax.servlet.http.HttpServletResponse;
    10 import javax.servlet.http.HttpServletResponseWrapper;
    11 
    12 public class CompressionWrapper extends HttpServletResponseWrapper {
    13     private GZipServletOutputStream gzServletOutputStream;
    14     private PrintWriter printWriter;
    15     public CompressionWrapper(HttpServletResponse response) {
    16         super(response);
    17         // TODO Auto-generated constructor stub
    18         
    19     }
    20     public ServletOutputStream getOutputStream()throws IOException{
    21         if(printWriter!=null){
    22             throw new IllegalStateException();//已调用过getWriter(),再调用getOutputStream()就抛出异常
    23         }
    24         if(gzServletOutputStream==null){
    25             gzServletOutputStream=new GZipServletOutputStream(getResponse().getOutputStream());
    26             //创建有压缩功能的GZipServletOutputStram对象    
    27         }
    28         return gzServletOutputStream;
    29     }
    30     public PrintWriter getWriter() throws IOException{
    31         if(gzServletOutputStream!=null){
    32             throw new IllegalStateException();//已调用过getOutputStream(),再调用getWriter()就抛出异常
    33         }
    34         if(printWriter==null){
    35             gzServletOutputStream=new GZipServletOutputStream(getResponse().getOutputStream());
    36             OutputStreamWriter osw= new OutputStreamWriter(gzServletOutputStream,getResponse().getCharacterEncoding());
    37             //创建GzipServletOutputStream对象,供构造PrintWriter时使用
    38             printWriter=new PrintWriter(osw);
    39         }
    40         return printWriter;
    41     }
    42     
    43     public void setContentLength(int len){}//不实现此方法内容,因为真正的输出会压缩
    44     public GZIPOutputStream getGZIPOutputStream(){
    45         if(this.gzServletOutputStream==null){
    46             return null;
    47         }
    48         return this.gzServletOutputStream.getGzipOutputStream();
    49     }
    50 }
    CompressionWrapper.java

      在同一个请求期间,getWriter()和getOutputStream()只能选择一个调用。接下来实现一个压缩过滤器,使用上面开发的CompressionWrapper来封装原HttpServletResponse。

     1 package ServletAPI;
     2 
     3 import java.io.IOException;
     4 import java.util.zip.GZIPOutputStream;
     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.annotation.WebFilter;
    13 import javax.servlet.http.HttpServletRequest;
    14 import javax.servlet.http.HttpServletResponse;
    15 
    16 /**
    17  * Servlet Filter implementation class CompressionFilter
    18  */
    19 @WebFilter(filterName="CompressionFilter",urlPatterns="/*")
    20 public class CompressionFilter implements Filter {
    21 
    22     /**
    23      * Default constructor. 
    24      */
    25     public CompressionFilter() {
    26         // TODO Auto-generated constructor stub
    27     }
    28 
    29     /**
    30      * @see Filter#destroy()
    31      */
    32     public void destroy() {
    33         // TODO Auto-generated method stub
    34     }
    35 
    36     /**
    37      * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
    38      */
    39     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    40         // TODO Auto-generated method stub
    41         // place your code here
    42 
    43         // pass the request along the filter chain
    44         HttpServletRequest req=(HttpServletRequest)request;
    45         HttpServletResponse res=(HttpServletResponse)response;
    46         String encoding=req.getHeader("accept-encoding");
    47         
    48         if((encoding!=null)&&(encoding.indexOf("gzip")>-1)){//检查是否接受gzip压缩
    49             CompressionWrapper responseWrapper=new CompressionWrapper(res);//创建响应封装器
    50             responseWrapper.setHeader("content-encoding","gzip");//设置响应内容编码为gzip格式
    51             chain.doFilter(request, responseWrapper);//下一个过滤器
    52             GZIPOutputStream gzipOutputStream=responseWrapper.getGZIPOutputStream();
    53             if(gzipOutputStream!=null){
    54                 gzipOutputStream.finish();//调用GZIPOutputStream的finish方法来完成压缩输出。
    55             }
    56         }else{
    57             chain.doFilter(request, response);//不接受压缩,直接进行下一个过滤器
    58         }
    59     }
    60 
    61     /**
    62      * @see Filter#init(FilterConfig)
    63      */
    64     public void init(FilterConfig fConfig) throws ServletException {
    65         // TODO Auto-generated method stub
    66     }
    67 
    68 }
    CompressionFilter.java

      浏览器是否采用GZIP压缩的格式,可以通过检查accept-encoding请求标头中是否包括gzip字符串来判断,如果接受,就创建CompressionWrapper封装原对象,并设置content-encoding响应标头为gzip即可。接着调用doFilter()时,传入的响应对象为CompressionWrapper对象。当doFilter()结束时,必须调用GZIPOutputStream的finish()方法,这才会将GZIP后的资料从缓冲区中全部移除并进行响应。

        如果客户端不接受GZIP压缩,就直接调用FilterChain的doFilter()。

  • 相关阅读:
    Redis使用小结
    MongoDB中的变更通知
    发布一个从迅雷下载字幕的小工具
    在Windows下通过netsh命令实现端口映射
    .net core在Linux ARM板上运行
    通过WinAPI播放PCM声音
    spring中scope作用域(转)
    Spring <context:annotation-config/> 解说(转)
    Jenkins+Maven+SVN快速搭建持续集成环境(转)
    maven中跳过单元测试(转)
  • 原文地址:https://www.cnblogs.com/liuzhongfeng/p/6107443.html
Copyright © 2020-2023  润新知