Filter的应用例子

1、内容替换Filter

有时候需要对网站的内容进行替换,防止输出非法内容或敏感内容。常规的解决方法是在保存数据进数据库前对非法敏感内容进行替换,或者在servlet里输出到客户端时进行替换。这两种解决方案都有很大的局限性,例如每个servlet都要进行替换、工作量大,与业务耦合度比较严重等。


使用内容替换Filter则方便许多。内容替换Filter的工作原理是,在servlet将内容输出到response时,response将内容缓存起来,在Filter中进行替换,然后在输出到客户端浏览器。由于默认的response并不能严格缓存输出内容,因此需要自定义一个具备缓存功能的response。

HttpCharacterResponseWrapper.java

package bqh.cslg;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class HttpCharacterResponseWrapper extends HttpServletResponseWrapper {

    //字符数组writer
    private CharArrayWriter charArrayWriter = new CharArrayWriter();

    public HttpCharacterResponseWrapper(HttpServletResponse response) {
        super(response);
    }

    //覆盖父类方法
    @Override
    public PrintWriter getWriter() throws IOException {
        return new PrintWriter(charArrayWriter);
    }

    public CharArrayWriter getCharArrayWriter() {
        return charArrayWriter;
    }
}

该类覆盖了getWriter()方法,当servlet中使用该response对象调用getWriter()来输出内容时,内容将被输出到CharArrayWriter中,达到缓存效果。

Filter需要将自定义的response传到servlet中,代码如下:

package bqh.cslg;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class OutputReplaceFilter implements Filter {

    private Properties pp = new Properties();

    public void init(FilterConfig filterconfig) throws ServletException {
        //获取web.xml中初始化参数
        String file = filterconfig.getInitParameter("file");
        //文件实际位置
        String realPath = filterconfig.getServletContext().getRealPath(file);
        try {
            //加载非法词
            pp.load(new FileInputStream(realPath));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void doFilter(ServletRequest servletrequest,
            ServletResponse servletresponse, FilterChain filterchain)
            throws IOException, ServletException {
        //自定义response
        HttpCharacterResponseWrapper response = new HttpCharacterResponseWrapper(
                (HttpServletResponse) servletresponse);
        //将自定义response传到servlet中
        filterchain.doFilter(servletrequest, response);
        //得到response输出内容
        String output = response.getCharArrayWriter().toString();
        //遍历敏感词
        for (Object obj : pp.keySet()) {
            String key = (String) obj;
            output = output.replace(key, pp.getProperty(key));
        }

        PrintWriter out = servletresponse.getWriter();
        //输出
        out.write(output);
    }

    public void destroy() {
        pp = null;
    }
}

web.xml配置

<filter>
  <filter-name>OutputReplaceFilter</filter-name>
  <filter-class>bqh.cslg.OutputReplaceFilter</filter-class>
  <init-param>
    <param-name>file</param-name>
    <param-value>/WEB-INF/sensitive.properties</param-value>
  </init-param>
  </filter>
  <filter-mapping>
    <filter-name>OutputReplaceFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>



WEB-INF下定义非法词库
sensitive.properties

Chna=China
\u8272\u60C5=**
\u66B4\u529B=**
www.baidu.com.cn=www.baidu.com

这里会自动将中文编程Unicode编码

测试:MyServlet

package bqh.cslg;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
        out.println("<HTML>");
        out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
        out.println("  <BODY>");
        out.println(request.getParameter("p1") + "<br>");
        out.println(request.getParameter("p2") + "<br>");
        out.println(request.getParameter("p3") + "<br>");
        out.println(request.getParameter("p4") + "<br>");
        out.println("  </BODY>");
        out.println("</HTML>");
        out.flush();
        out.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        this.doGet(request, response);
    }
}

浏览器直接访问http://127.0.0.1:8080/TextChangeFilter/servlet/MyServlet?p1=Chna&p2=www.baidu.com.cn&p3=%E8%89%B2%E6%83%85&p4=%E6%9A%B4%E5%8A%9B
效果图

可以看到自定义的敏感词已经被代替。





2、GZIP压缩Filter
使用Filter实现GZIP压缩,需要自定义的response和servletOutputStream。

压缩Filter中需要先判断客户浏览器是否支持GZip的自动解压,如果支持,则进行GZIP压缩,否则不压缩。判断的依据是浏览器提供的header信息。

GZipFilter

package bqh.cslg;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebFilter("/*")
public class GZipFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;

        //支持的编码方式
        String acceptEncoding = request.getHeader("Accept-Encoding");
        if (acceptEncoding != null
                && acceptEncoding.toLowerCase().indexOf("gzip") != -1) {
            //客户端浏览器支持GZIP压缩
            GZipResponseWrapper gzipRes = new GZipResponseWrapper(response);
            chain.doFilter(request, gzipRes);
            gzipRes.finishResponse();
        } else {
            //客户端浏览器不支持GZIP压缩
            chain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }

}

GZipResponseWrapper

package bqh.cslg;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GZipResponseWrapper extends HttpServletResponseWrapper {

    private HttpServletResponse response;
    // 自定义的outputstream,执行close的时候对数据压缩,并输出
    private GZipOutputStream gzipOutputStream;
    // 自定义的PrintWriter,将内容输出到GZipOutputStream中
    private PrintWriter writer;

    public GZipResponseWrapper(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    // 覆盖getOutputStream(),处理二进制内容
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (gzipOutputStream == null) {
            gzipOutputStream = new GZipOutputStream(response);
        }
        return gzipOutputStream;
    }

    // 覆盖getWriter(),处理字符内容
    @Override
    public PrintWriter getWriter() throws IOException {
        if (writer == null) {
            writer = new PrintWriter(new OutputStreamWriter(
                    new GZipOutputStream(response), "utf-8"));
        }
        return writer;
    }

    // 压缩后数据长度发生变化,因此将该方法置为空
    @Override
    public void setContentLength(int len) {
    }

    @Override
    public void flushBuffer() throws IOException {
        gzipOutputStream.flush();
    }

    public void finishResponse() throws IOException {
        if (gzipOutputStream != null) {
            gzipOutputStream.close();
        }
        if (writer != null) {
            writer.close();
        }
    }
}

GZipResponseWrapper

package bqh.cslg;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GZipResponseWrapper extends HttpServletResponseWrapper {

    private HttpServletResponse response;
    // 自定义的outputstream,执行close的时候对数据压缩,并输出
    private GZipOutputStream gzipOutputStream;
    // 自定义的PrintWriter,将内容输出到GZipOutputStream中
    private PrintWriter writer;

    public GZipResponseWrapper(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    // 覆盖getOutputStream(),处理二进制内容
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (gzipOutputStream == null) {
            gzipOutputStream = new GZipOutputStream(response);
        }
        return gzipOutputStream;
    }

    // 覆盖getWriter(),处理字符内容
    @Override
    public PrintWriter getWriter() throws IOException {
        if (writer == null) {
            writer = new PrintWriter(new OutputStreamWriter(
                    new GZipOutputStream(response), "utf-8"));
        }
        return writer;
    }

    // 压缩后数据长度发生变化,因此将该方法置为空
    @Override
    public void setContentLength(int len) {
    }

    @Override
    public void flushBuffer() throws IOException {
        gzipOutputStream.flush();
    }

    public void finishResponse() throws IOException {
        if (gzipOutputStream != null) {
            gzipOutputStream.close();
        }
        if (writer != null) {
            writer.close();
        }
    }
}

GZipOutputStream

package bqh.cslg;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

public class GZipOutputStream extends ServletOutputStream {

    // 原response
    private HttpServletResponse response;
    // JDK自带的GZIP压缩数据类
    private GZIPOutputStream gzipOutputStream;
    // 将压缩后的数据存放到ByteArrayOutputStream中
    private ByteArrayOutputStream byteArrayOutputStream;

    public GZipOutputStream(HttpServletResponse response) throws IOException {
        this.response = response;
        byteArrayOutputStream = new ByteArrayOutputStream();
        gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
    }

    // 输出到JDK的GZIP类中
    @Override
    public void write(int b) throws IOException {
        gzipOutputStream.write(b);
    }

    // 执行压缩,并将数据输出到浏览器
    public void close() throws IOException {
        // 执行压缩一定要调用该方法
        gzipOutputStream.finish();

        // 将压缩后的数据输出到客户端
        byte[] content = byteArrayOutputStream.toByteArray();

        // 设定压缩方式为GZIP,客户端浏览器会自动解压缩
        response.addHeader("Content-Encoding", "gzip");
        response.addHeader("Content-Length", Integer.toString(content.length));

        // 输出到浏览器
        ServletOutputStream out = response.getOutputStream();
        out.write(content);
        out.close();
    }

    public void flush() throws IOException {
        gzipOutputStream.flush();
    }

    public void write(byte[] b, int off, int len) throws IOException {
        gzipOutputStream.write(b, off, len);
    }

    public void write(byte[] b) throws IOException {
        gzipOutputStream.write(b);
    }
}

测试类:gzip.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@page import="java.net.URLConnection"%>
<%@page import="java.net.URL"%>
<%@page import="java.text.NumberFormat"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">
body {
    margin-top: 2px; 
}
table {
    margin-top: 10px; 
    border-collapse: collapse;
    border: 1px solid #000000; 
    width: 350px; 
}
td{
    border: 1px solid #000000; 
    font-size: 12px; 
    padding: 2px; 
    background: #DDDDFF;
}
</style>
</head>
<body>
<%!
public void test(JspWriter out, String url) throws Exception {

    // 模拟支持 GZIP 的浏览器
    URLConnection connGzip = new URL(url).openConnection();
    connGzip.setRequestProperty("Accept-Encoding", "gzip");
    int lengthGzip = connGzip.getContentLength();

    // 模拟不支持 GZIP 的浏览器
    URLConnection connCommon = new URL(url).openConnection();
    int lengthCommon = connCommon.getContentLength();

    double rate = new Double(lengthGzip) / lengthCommon;

    out.println("<table>");
    out.println("   <tr>");
    out.println("       <td colspan=3>网址: " + url + "</td>");
    out.println("   </tr>");
    out.println("   <tr>");
    out.println("       <td>压缩后:" + lengthGzip + " byte</td>");
    out.println("       <td>压缩前:" + lengthCommon + " byte</td>");
    out.println("       <td>比率:" + NumberFormat.getPercentInstance().format(rate) + "</td>");
    out.println("   </tr>");
    out.println("</table>");
}
%>
<%
String[] urls = {
        "http://localhost:8080/GZIPFilter/1.png", 
        "http://www.sina.com.cn/", 
        "https://v.qq.com/", 
};
for(String url : urls){
    test(out, url);
}
%>

</body>
</html>

效果图

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值