JavaWeb 过滤器

 一、Filter 简

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

 

二、Filter 过滤实现步骤

       Servlet API 中提供了一个 Filter 接口,开发 Web 应用时,如果编写的 Java 类实现了这个接口,则把这个 Java 类称之为过滤器 Filter。通过 Filter 技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。Filter 的生命周期类似于 Servlet ,但是 Servlet 是在首次访问的时候就被创建而 Filter 是服务器启动的时候就会被创建。

 

        

           1、览器向服务器发送请求

           2、Web 服务器收到请求和响应对象转发给 Filter。Filter 是在服务器启动的时候就已经创建,在创建的时候会调用 init 方法对 Filter 进行初始化。之后执行 doFilter 对已经拦截的请求进行处理。

           3、Filter 调用过滤器链 chain.doFilter 方法将拦截到的请求放行,转发给要访问的 Web 资源。

           4、当服务器关闭的时候会调用 Filter 的 destroy 方法摧毁过滤器。

 

三、过滤器链

       如果有多个过滤器,那么服务器会按照 web.xml 文件中配置过滤器的顺序依次执行过滤器。如果现有两个 Filter,当 Filter A 截到请求响应以后,当执行到 chain.doFilter(request, response) 的时候,FilterChain 对象就是过滤器链会检查是否有下一个 FIlter,如果有下一个 Filter 过滤器链会执行 doFilter 方法执行下一个 Filter。那么 A、B 两个过滤器的执行顺序应是:Filter A 执行,Filter B 执行,执行要访问的 Web 资源,Filter B 执行完成,Filter A 执行完成。

package cn.dk.filter;

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;

public class MyFilter implements Filter {

	public void destroy() {
		System.out.println("Filter 被摧毁");
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("Filter 执行之前");
		chain.doFilter(request, response);
		System.out.println("Filter 执行之后");
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("Filter 被创建");
	}
}


四、过滤器实现解决全站乱码

       要解决全站的乱码,我们需要知道两个问题,一是乱码的原因,二是过滤器如何解决。

        1、户端表单的提交方式是 post,那么我们只需要在服务器端设置 request.setCharacterEncoding(); 就可以解决客户端传递过来的数据乱码。因为当客户端以 post 方式提交数据的时候,计算机会根据浏览器当前使用的码表对数据进行编码,然后以二进制的形式传递给服务器,服务器接受到数据以后,在没有指定码表的时候,服务器会依据 iso-8859-1 的形式进行解码,所以会出现乱码现象,由此我们可以将服务器解码用的码表设置为和浏览器一样则可以解决乱码现象。但是如果客户端提交表单的形式为 get 方式,那么表单的数据会经过 utl 编码后附带在 url 地址栏后面提交给服务器,此时采用的是 iso-8859-1 的编码形式,这时候我们即便是将服务器的解码方式设置为和浏览器同步,也无法获取到真实数据。我们只能先将服务器解码后的字符串再以 iso-8859-1 码表编码,得到数据的元信息,之后再经过 UTF-8 解码才能得到想要的中文。

        2、如果网站有几百个页面,那么我们每个页面手工处理乱码则非常耗时,如果我们使用 Filter 技术,则可以一处解决到处使用。它的原理是:我们将客户端的请求通过 Filter 拦截后,获取到请求信息的 request 对象,对 request 对象进行了修改后再转发给 Web 服务器中的其它资源,这样其它资源再引用 request 获取数据的时候无需再手工处理乱码现象。然而 request 对象中只有处理 post 方式乱码的解决方案,并没有办法处理 get 方式的乱码,此时我们就不能再使用 SUN 公司为我们提供的 request 对象,我们需要动态的为 request 增加新的功能,增加处理 post 乱码的功能。这个方案有三种解决途径:写 request 的子类,装饰模式,动态代理。分析上面三种,后两者可取。

首先用装饰模式解决乱码:

package cn.dk.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
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.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

public class CharactersetFilter implements Filter {

	private FilterConfig config;

	public void destroy() {
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		
		response.setCharacterEncoding(config.getInitParameter("charset"));
		String characterEncoding = request.getCharacterEncoding();
		if (characterEncoding !=null && characterEncoding.equalsIgnoreCase(config
				.getInitParameter("charset"))) {
			chain.doFilter(request, response);
			return;
		}
		chain.doFilter(new MyRequest(request, config), response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		this.config = filterConfig;
	}
}

class MyRequest extends HttpServletRequestWrapper {
	
	private HttpServletRequest request;

	public MyRequest(HttpServletRequest request, FilterConfig config) {
		super(request);
		this.config = config;
		this.request = request;
	}

	private FilterConfig config;

	public String getParameter(String name) {
		String parameter = this.request.getParameter(name);
		try {
			return parameter = new String(parameter.getBytes("iso-8859-1"),
					config.getInitParameter("charset"));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public Map<String, String[]> getParameterMap() {
		Map<String, String[]> parameterMap = this.request.getParameterMap();
		Map<String, String[]> newParameterMap = new HashMap<String, String[]>();
		for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
			String[] parameters = entry.getValue();
			String[] newParamters = new String[parameters.length];
			for (int i = 0; i < parameters.length; i++) {
				try {
					newParamters[i] =  new String(parameters[i].getBytes("iso-8859-1"), config
							.getInitParameter("charset"));
				} catch (UnsupportedEncodingException e) {
					throw new RuntimeException(e);
				}
			}
			newParameterMap.put(entry.getKey(), newParamters);
		}
		return newParameterMap;
	}

	public String[] getParameterValues(String name) {
		String[] parameterValues = this.request.getParameterValues(name);
		String[] newParameterValues = new String[parameterValues.length];
		for (int i = 0; i < parameterValues.length; i++) {
			String value = parameterValues[i];
			try {
				newParameterValues[i] = new String(
						value.getBytes("iso-8859-1"), config
								.getInitParameter("charset"));
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}
		}
		return newParameterValues;
	}
}


动态代理解决:

package cn.dk.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
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.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

public class CharactersetFilter implements Filter {

	private FilterConfig config;

	public void destroy() {
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		final HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		response.setCharacterEncoding(config.getInitParameter("charset"));
		String characterEncoding = request.getCharacterEncoding();
		if (characterEncoding != null
				&& characterEncoding.equalsIgnoreCase(config
						.getInitParameter("charset"))) {
			chain.doFilter((ServletRequest) Proxy.newProxyInstance(
					CharactersetFilter.class.getClassLoader(), request
							.getClass().getInterfaces(),
					new InvocationHandler() {

						@SuppressWarnings("unchecked")
						public Object invoke(Object proxy, Method method,
								Object[] args) throws Throwable {
							String methodName = method.getName();
							if (method.equals("getParameter")) {
								String value = (String) method.invoke(request, args);
								value = new String(value.getBytes("iso-8859-1"),"utf-8");
								return value;
							}else if(methodName.equals("getParameterMap")){
								Map<String, String[]> map = (Map<String, String[]>) method.invoke(request, args);
								Map<String, String[]> newMap = new HashMap<String, String[]>();
								for (Map.Entry<String, String[]> entry : map.entrySet()) {
									String[] params = entry.getValue();
									String[] newParams = new String[params.length];
									for (int i = 0; i < params.length; i++) {
										newParams[i] = new String(params[i].getBytes("iso-8859-1"),"utf-8");
									}
									newMap.put(entry.getKey(), newParams);
								}
								return newMap;
							}else if(methodName.equals("getParameterValues")){
								String[] parameterValues = (String[]) method.invoke(request, args);
								String[] newParameterValues = new String[parameterValues.length];
								for (int i = 0; i < parameterValues.length; i++) {
									String value = parameterValues[i];
									try {
										newParameterValues[i] = new String(
												value.getBytes("iso-8859-1"), config
														.getInitParameter("charset"));
									} catch (UnsupportedEncodingException e) {
										throw new RuntimeException(e);
									}
								}
								return newParameterValues;
							}
							return null;
						}

					}), response);
			return;
		}
		chain.doFilter(new MyRequest(request, config), response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		this.config = filterConfig;
	}
}


五、过滤器实现网站流量统计

       原理分析:如果要对一个页面的访问次数进行统计,我们必须要考虑到用哪个域来保存统计信息数据,只有 ServletContext 域才可以保存,因为其它域的生命周期不能满足我们的需求,只要服务器不停止,我们的统计数据都会被保存。找到了保存数据的容器,我们就要做一个过滤器来实现对网站资源的访问拦截。然后从 ServletContext 域中取出以 URI 为key 访问数量为 value 的 Map 集合。在进行增加统计数以后,我们再对客户端的访问请求放行。

package cn.dk.filter;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

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

public class PageCountFilter implements Filter {

	public void destroy() {

	}

	@SuppressWarnings("unchecked")
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		ServletContext context = request.getSession().getServletContext();
		String uri = request.getRequestURI();
		Map<String, Integer> map = (Map<String, Integer>) context
				.getAttribute("count");
		if (map == null)
			map = new LinkedHashMap<String, Integer>();
		Integer integer = map.get(uri);
		if (integer == null)
			map.put(uri, 1);
		else
			map.put(uri, integer + 1);
		context.setAttribute("count", map);
		chain.doFilter(request, response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {

	}
}

 

六、过滤器实现全站自动登录

       要实现网站自动登录,我们需要完成大体两个操作,一是在用户成功登录以后,向客户机发送 cookie 保存用户登录信息。二是在用户访问网站的每一个页面时实现过滤器拦截,处理 cookie 信息实现自动登录。

        1、当用户成功登录以后,我们将向用户浏览器发送一个包含登录名,有效期和密码的 cookie。登录名用以以后匹配密码,有效期用于判断自动登录失效时间,密码分为两个部分,一个是有效期常量,一个是真实密码,之后对两者混合 MD5 运算,保证客户机中的 cookie 不包含真实用户密码信息。

        2、当用户设置自动登录以后,在访问网站的每一个页面我们需要进行过滤器拦截。首先我们获取到用户带来的 cookie 信息,之后对 cookie 进行解析,分解为三个部分,首先判断是否超出自动登录有效期,用当前系统时间和保存在 cookie 中的时间进行比较。之后我们在数据库中查处用户名对应的密码,再进行 MD5 运算,与 cookie 带来的密码进行匹配,如果匹配成功,则在 session 域中保存用户登录信息。实现全站自动登录。

登录 Servlet:

 

package cn.dk.servlet;

import java.io.IOException;
import java.security.MessageDigest;

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

import sun.misc.BASE64Encoder;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import cn.dk.dao.UserDao;
import cn.dk.domain.User;

public class LoginServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 判断是否是已经登录用户
		Object attribute = request.getSession().getAttribute("user");
		if (attribute != null) {
			return;
		}

		// 获取用户名密码
		String name = request.getParameter("name");
		String pass = request.getParameter("pass");

		// 判断用户名密码是否正确
		UserDao dao = new UserDao();
		User user = dao.login(name, pass);
		if (user == null) {
			request.setAttribute("message", "用户名密码不正确");
			request.getRequestDispatcher("/message.jsp").forward(request,
					response);
			return;
		}

		// 获取自动登录时间
		int time = (int) (Integer.parseInt(request.getParameter("autologintime")) + System.currentTimeMillis());

		// 创建 md5内容
		String content = name + ":" + time + ":" + makeMD5(time, pass);

		// 构建cookie内容(name:md5(time_pass))
		Cookie cookie = new Cookie("auto_login", content);
		cookie.setMaxAge(time);
		cookie.setPath(request.getContextPath());
		response.addCookie(cookie);

		// 将登录信息存放于session中
		request.getSession().setAttribute("user", user);
		response.sendRedirect(request.getContextPath() + "/show.jsp");
	}

	private String makeMD5(long time, String pass) {
		try {
			MessageDigest disDigest = MessageDigest.getInstance("md5");
			String msg = time + "_" + pass;
			byte[] b = disDigest.digest(msg.getBytes());
			BASE64Encoder base = new BASE64Encoder();
			return base.encode(b);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

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

}


过滤器:

package cn.dk.filter;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

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.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import sun.misc.BASE64Encoder;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import cn.dk.dao.UserDao;
import cn.dk.domain.User;

public class AutoLogin implements Filter {

	public void destroy() {
	}

	public void doFilter(ServletRequest r, ServletResponse re, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) r;
		HttpServletResponse response = (HttpServletResponse) re;

		//判断是否是已登陆用户
		Object attribute = request.getSession().getAttribute("user");
		if(attribute != null){
			chain.doFilter(request, response);
			return;
		}
		
		//获取cookie
		Cookie[] cookies = request.getCookies();
		if(cookies == null){
			chain.doFilter(request, response);
			return;
		}
		Cookie cookie = null;
		for (int i = 0; i < cookies.length; i++) {
			if(cookies[i].getName().equals("auto_login")){
				cookie = cookies[i];
				//截取cookie信息
				String value = cookie.getValue();
				String[] values = value.split(":");
				int time = Integer.parseInt(values[1]);
				//判断是否超时
				int sys_time = (int) System.currentTimeMillis();
				if(sys_time>time){
					chain.doFilter(request, response);
					return;
				}
				
				//进行用户名密码的匹配
				String name = values[0];
				String pass = new UserDao().getPass(name);
				String md5 = values[2];
				String ser_md5 = makeMD5(time, pass);
				if(!md5.equals(ser_md5)){
					chain.doFilter(request, response);
					return;
				}
				
				//构建用户并存放在session中
				User user = new User();
				user.setName(name);
				request.getSession().setAttribute("user", user);
			}
		}
		
		
		// 放行
		chain.doFilter(request, response);
	}

	private String makeMD5(long time, String pass) {
		try {
			MessageDigest disDigest = MessageDigest.getInstance("md5");
			String msg = time + "_" + pass;
			byte[] b = disDigest.digest(msg.getBytes());
			BASE64Encoder base = new BASE64Encoder();
			return base.encode(b);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public void init(FilterConfig filterConfig) throws ServletException {

	}
}


七、过滤器实现敏感词汇过滤

       敏感词汇分为三种,一种是禁止词汇,如果出现禁止词汇,那么则拒绝提交。一种是审核词汇,如果出现审核词汇,则允许提交,但是我们要将要审核的关键词高亮显示。另一种是替换词汇,如果出现替换词汇我们将采用特殊字符替换掉原来词汇。由于后两者要获取 request 中的数据进行处理,所以我们应该增强 request 中的方法。第一种我们可以直接判断之后禁止提交即可。

         难点:我们将词汇放在一个目录下,之后让目录以源代码的形式存在于 classes 目录下方便我们取出。之后我们将对敏感词汇逐行遍历,我们采用将敏感词汇以正则表达式的方式与提交的文本进行匹配。为了防止恶意输出,我们在每个字符之间加上 " .{0,10} " 过滤掉两个字之间的任何字符。之后进行比较。

 

package cn.dk.filter;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

public class WordFilter implements Filter {

	private List<String> banWords = new ArrayList<String>(); // 禁止词汇
	private List<String> auditWords = new ArrayList<String>(); // 审核词汇
	private List<String> replace = new ArrayList<String>(); // 替换词汇

	public void destroy() {

	}

	public void doFilter(ServletRequest r, ServletResponse re, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) r;
		HttpServletResponse response = (HttpServletResponse) re;
		
		Enumeration<String> names = request.getParameterNames();
		while(names.hasMoreElements()){
			String name = names.nextElement();
			for (String string : banWords) {
				Pattern pattern = Pattern.compile(string);
				String value = request.getParameter(name);
				Matcher m = pattern.matcher(value);
				if(m.find()){
					try {
						request.setAttribute("message", "不能包含非法词汇");
						request.getRequestDispatcher("/message.jsp").forward(request, response);
						return;
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
		}
		chain.doFilter(new Myrequest(request), response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		// 加载敏感词汇到三个容器中
		String path = WordFilter.class.getClassLoader().getResource("")
				.getPath();
		File dir = new File(path);
		File[] listFiles = dir.listFiles();
		for (File file : listFiles) {
			if (file.isFile()) {
				try {
					BufferedReader reader = new BufferedReader(new FileReader(
							file));
					String line = null;
					while ((line = reader.readLine()) != null) {
						String[] words = line.split("\\|");
						if (words.length != 2)
							return;
						String type = words[1].trim();
						if (type.equals("1"))
							banWords.add(words[0]);
						else if (type.equals("2"))
							auditWords.add(words[0]);
						else if (type.equals("3"))
							replace.add(words[0]);
					}
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
		}
	}

	class Myrequest extends HttpServletRequestWrapper {

		private HttpServletRequest request;

		public Myrequest(HttpServletRequest request) {
			super(request);
			this.request = request;
		}

		public String getParameter(String name) {
			String value = request.getParameter(name);
			for (String string : auditWords) {
				Pattern pattern = Pattern.compile(string);
				Matcher m = pattern.matcher(value);
				while(m.find()){
					try {
						return value = value.replaceAll(value, "<font color='red'>" + value + "</font>");
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
			for (String string : replace) {
				Pattern pattern = Pattern.compile(string);
				Matcher m = pattern.matcher(value);
				while(m.find()){
					try {
						return value = value.replaceAll(value, "@@@");
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
			return null;
		}
	}
}

 

八、过滤器实现全站输出压缩

       在大型网站中,为了节省带宽,提高服务器响应速度,我们应该将服务器发送出的数据先进行压缩后再打给浏览器。要实现全站压缩输出,我们应该创建一个过滤器,过滤一切 response 的输出操作。由于当服务器在调用 response.writer 和 response.getOutpuStream 时是在缓存满了以后直接打给客户机浏览器的,所以我们无法进行压缩操作。我们只有自己写一个 response ,让之后所有的 response 都能实现压缩输出。

         思路分析:在用户请求服务器时,我们应该准备一个自己的 response 之后转发给下面的资源,在下面的资源进行操作完毕的时候,还是要经过过滤器才能输出。因此在这时,我们再通过拿到刚才写在 response 中的数据,取出压缩后再向浏览器输出。

         难点:我们创建一个新的 response 目的就是控制 outputStream 的行为,所以我们在自己的 response 中应该重写 getOutputStream  方法,该方法返回的是 ServletOutputStream ,因为 ServletOutputStream 不允许被实例化,所以我们又不得不再写一个自己的 ServletOutputStream 从而实现 write 方法。当用户调用 response.getOutputStream.write() 方法时,实际上是在我们自定义 ServletOutputStream 中的 write 中写。这时我们应该让数据写到一个缓冲区中,这个缓冲区是上一级维护的,所以我们应该在构造方法中接受这个缓冲区。最后我们要获得缓冲区中的数据,再经过 Gzip 压缩后打给浏览器显示。

         另外我们还应该写一个新的 printWriter 保证当用户使用字符流时一样也能压缩输出。与上面字节流不同的是字符流可以被实例化,这样我们可以直接传递一个缓冲区进去,为了防止乱码,我们还应该使用包装流为缓冲区包装。由于 printWriter 也是一个包装流,所以我们不要忘了将它的缓冲区刷新掉。

 

package cn.dk.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GzipFilter implements Filter {

	public void destroy() {
	}

	public void doFilter(ServletRequest r, ServletResponse rs, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) r;
		HttpServletResponse response = (HttpServletResponse) rs;

		// 创建自己的response
		MyResponse myResponse = new MyResponse(response);
		// 执行放行
		chain.doFilter(request, myResponse);
		
		byte[] data = myResponse.getData();
		System.out.println("原始大小:" + data.length);
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		GZIPOutputStream gzip = new GZIPOutputStream(bos);
		gzip.write(data);
		gzip.close();
		System.out.println("压缩后大小:" +bos.toByteArray().length);
		response.setHeader("content-encoding", "gzip");
		response.setHeader("content-length", bos.toByteArray().length + "");
		response.getOutputStream().write(bos.toByteArray());
	}

	public void init(FilterConfig filterConfig) throws ServletException {
	}

	class MyResponse extends HttpServletResponseWrapper {
		
		private ByteArrayOutputStream bos = new ByteArrayOutputStream();
		private PrintWriter pw;

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

		public ServletOutputStream getOutputStream() throws IOException {
			// 创建自己的servletOutputStream
			return new MyServletOutputStream(bos);
		}
		
		public PrintWriter getWriter() throws IOException {
			pw = new PrintWriter(new OutputStreamWriter(bos,"utf-8"));
			return pw;
		}

		public byte[] getData(){
			pw.close();
			return bos.toByteArray();
		}
	}

	class MyServletOutputStream extends ServletOutputStream {
		
		public MyServletOutputStream(ByteArrayOutputStream bos) {
			super();
			this.bos = bos;
		}

		private ByteArrayOutputStream bos;

		public void write(int b) throws IOException {
			bos.write(b);
		}
	}
}



九、动态代理实现全站压缩

package cn.dk.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.zip.GZIPOutputStream;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Gzip2 implements Filter {

	public void destroy() {
	}

	public void doFilter(ServletRequest r, ServletResponse rs,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) r;
		final HttpServletResponse response = (HttpServletResponse) rs;
		final ByteArrayOutputStream bos = new ByteArrayOutputStream();
		HttpServletResponse myResponse = (HttpServletResponse) Proxy.newProxyInstance(response.getClass().getClassLoader(), response.getClass().getInterfaces(), new InvocationHandler(){
			public Object invoke(final Object responseProxy, Method method, Object[] args)
					throws Throwable {
				String methodName = method.getName();
				System.out.println(methodName);
				if(methodName.equals("getOutputStream")){
					return new MyOutputStream(bos);
				}else{
					return method.invoke(response, args);
				}
			}
		});
		chain.doFilter(request, myResponse);
		byte[] byteArray = bos.toByteArray();
		System.out.println("压缩前:" + byteArray.length);
		ByteArrayOutputStream b = new ByteArrayOutputStream();
		GZIPOutputStream gzip = new GZIPOutputStream(b);
		gzip.write(byteArray);
		gzip.close();
		System.out.println("压缩后:" + b.toByteArray().length);
		response.setHeader("content-encoding", "gzip");
		response.setHeader("content-length", bos.toByteArray().length + "");
		response.getOutputStream().write(b.toByteArray());
	}
	
	class MyOutputStream extends ServletOutputStream{
		private ByteArrayOutputStream bos;

		public MyOutputStream(ByteArrayOutputStream bos) {
			super();
			this.bos = bos;
		}

		public void write(int b) throws IOException {
			try {
				bos.write(b);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}

	public void init(FilterConfig filterConfig) throws ServletException {
	}
}



 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值