将琴存诗
人生 可以不要那么 耀 ,只需要有 一个  平凡的梦想  足以 。—— loveincode -_^ RSS
Fork me on GitHub

JavaWeb 后端 <十二> 之 过滤器 filter 乱码、不缓存、脏话、标记、自动登录、全站压缩过滤器

1. 过滤器是什么?有什么?

1、过滤器属于Servlet规范,从2.3版本就开始有了。

2、过滤器就是对访问的内容进行筛选(拦截)。利用过滤器对请求和响应进行过滤

2. 编写步骤和执行过程

1、编码步骤:

a、编写一个类:实现javax.servlet.Filter接口

public class FilterDemo1 implements Filter {
	
	public FilterDemo1(){
		System.out.println("调用了默认的构造方法");
	}
	
	//用户每次访问被过滤的资源,都会由服务器调用该方法实现过滤
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("FilterDemo1执行过滤前");
		//对请求进行拦截,代码写在这
		chain.doFilter(request, response);//放行
		//对响应进行拦截,代码写在这
		System.out.println("FilterDemo1执行过滤后");
	}

	public void destroy() {
		System.out.println("调用了销毁方法");
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("调用初始化方法");
	}

}

 b、配置web.xml,指定需要过滤的资源。(和Servlet的配置相当类似)

2、过滤器的执行过程(生命周期)

生命周期:

诞生:过滤器的实例是在应用被加载时就完成的实例化,并初始化的。

存活:和应用的生命周期一致的。在内存中是单例的。针对拦截范围内的资源访问,每次访问都会调用void doFIlter(request,response.chain)进行拦截。

死亡:应用被卸载。

 

3. 串联过滤器

一个过滤器接着另外一个过滤器。执行的顺序  按照web.xml的先后顺序

随意 访问  会先直接FilterDemo2 的 再执行 FilterDemo3的

会输出 :

FilterDemo2前

FilterDemo3前

执行内容

FilterDemo3后

FilterDemo2后

4. 案例:

1、解决请求参数(POST)和响应输出的乱码过滤器

//解决post方式请求参数和响应编码问题的过滤器
public class SetCharacterEncodingFilter implements Filter {
	private FilterConfig filterConfig;
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		
		String encoding = filterConfig.getInitParameter("encoding");//用户可能忘记了配置该参数
		if(encoding==null){
			encoding = "UTF-8";//默认编码
		}
		
		request.setCharacterEncoding(encoding);//只能解决POST请求参数的中文问题
		response.setCharacterEncoding(encoding);//输出流编码
		response.setContentType("text/html;charset="+encoding);//输出流编码,通知了客户端应该使用的编码
		chain.doFilter(request, response);
	}

	public void destroy() {

	}

}

将 编码类型写在 Filter参数中

 

 

2、动态资源不要缓存的过滤器

Servlet/JSP:动态资源不要缓存。

 

这里 将 Servletrequest 和 ServletResponse 转换为 HttpServlet 的方法 避免发生错误

HttpServletRequest request = null;
HttpServletResponse response = null;
try{
  request  = (HttpServletRequest)req;
  response = (HttpServletResponse)resp;
}catch(Exception e){   throw new RuntimeException("not-http request or response"); }

 

//控制动态资源不要缓存过滤器
public class NoCacheFilter implements Filter {

	public void init(FilterConfig filterConfig) throws ServletException {

	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
//		HttpServletRequest request = (HttpServletRequest)req;
//		HttpServletResponse response = (HttpServletResponse)resp;   不使用该方法
		
		HttpServletRequest request = null;
		HttpServletResponse response = null;
		try{
			request  = (HttpServletRequest)req;
			response = (HttpServletResponse)resp;
		}catch(Exception e){
			throw new RuntimeException("not-http request or response");
		}
		response.setHeader("Expires", "-1");
		response.setHeader("Cache-Control", "no-cache");
		response.setHeader("Pragma", "no-cache");
		chain.doFilter(request, response);
	}

	public void destroy() {

	}

}

动态过滤掉 Servlet 和Jsp

3、静态资源控制缓存时间的过滤器

//控制静态资源的缓存时间
public class StaticResourcesNeedCacheFilter implements Filter {
	private FilterConfig filterConfig;
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = null;
		HttpServletResponse response = null;
		try{
			request  = (HttpServletRequest)req;
			response = (HttpServletResponse)resp;
		}catch(Exception e){
			throw new RuntimeException("not-http request or response");
		}
		long time = 0;//缓存的时间
		
		//根据用户请求的uri地址的后缀:/day19_00_filter/1.html
		String uri  = request.getRequestURI();
		String exName = uri.substring(uri.lastIndexOf(".")+1);
		if("html".equals(exName)){
			String value = filterConfig.getInitParameter("html");//小时
			time = Long.parseLong(value)*60*60*1000;
		}
		if("css".equals(exName)){
			String value = filterConfig.getInitParameter("css");//小时
			time = Long.parseLong(value)*60*60*1000;
		}
		if("js".equals(exName)){
			String value = filterConfig.getInitParameter("js");//小时
			time = Long.parseLong(value)*60*60*1000;
		}
		
		response.setDateHeader("Expires", System.currentTimeMillis()+time);  //Expires 控制时间
		chain.doFilter(request, response);
	}

	public void destroy() {

	}

}

 

4、//*用户自动登录过滤器:

使用了Md5 加密

Base64编码:很重要

编写

//自动登录过滤器
public class AutoLoginFilter implements Filter {
	private  BusinessService s = new BusinessServiceImpl();
	public void init(FilterConfig filterConfig) throws ServletException {

	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = null;
		HttpServletResponse response = null;
		try{
			request  = (HttpServletRequest)req;
			response = (HttpServletResponse)resp;
		}catch(Exception e){
			throw new RuntimeException("not-http request or response");
		}
		HttpSession session = request.getSession();
		//判断用户有没有登录:只管没有登录的
		User sUser = (User)session.getAttribute("user");
		if(sUser==null){
			//找loginInfo的cookie:只管找到的
			Cookie cs[] = request.getCookies();
			for(int i=0;cs!=null&&i<cs.length;i++){
				if("loginInfo".equals(cs[i].getName())){
					//解出用户名(Base64)和密码(MD5)
					String usernamePassword = cs[i].getValue();
					String username = usernamePassword.split("_")[0];//base64编码后的
					String password = usernamePassword.split("_")[1];//MD5加密后的
					//调用Service再次验证正确性
					User user = s.login(SecurityUtil.base64decode(username), password);
					//通过:登录。在HttpSession中设置登录标记
					if(user!=null){
						session.setAttribute("user", user);
					}
				}
			}
		}
		chain.doFilter(request, response);
	}

	public void destroy() {

	}

}

 

5. 过滤器配置的细节

 

6. 巩固装饰设计模式

一、装饰

1、编写一个类,实现与被包装类(数据库驱动对Connection的实现)相同的接口。(使这个类和数据库的驱动实现有着相同的行为)

2、定义一个变量,引用被包装类的实例。

3、定义构造方法,传入被包装类的实例。

4、对于要改写的方法,编写自己的代码即可。

5、对于不需要改写的方法,调用原有对象的对应方法。

二、装饰变体(BufferedReader本身就是包装类,对Reader的包装。LineNumberReader,对BufferedReader的包装,还是他的子类)

1、编写一个类,继承已经是包装类的类。

2、定义一个变量,引用被包装类的实例。

3、定义构造方法,传入被包装类的实例。

4、覆盖掉需要改写的方法

7. 案例:

1、解决全站中文乱码过滤器

之前解决了 post 的乱码 问题 这里 添加 get的 解决问题

1.定义一个类 EncodingHttpServletRequest 继承 HttpServletRequestWrapper  前对 后 的 包装 得到自己想要的 方法

2.重写  HttpServletRequestWrapper 的 getParameter 方法 。

3.将get传入的值使用 该 super.getCharacterEncoding()的编码 方式返回

如果 使用 get方法 就可以 得到 应有的编码方式

public class SetCharacterEncodingFilter implements Filter {
	private FilterConfig filterConfig;
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request;
		HttpServletResponse response;
		try{
			request = (HttpServletRequest)req;
			response = (HttpServletResponse)resp;
		}catch(Exception e){
			throw new RuntimeException("non-http request or response");
		}
		
		String encoding = filterConfig.getInitParameter("encoding");//用户可能忘记了配置该参数
		if(encoding==null){
			encoding = "UTF-8";//默认编码
		}
		
		request.setCharacterEncoding(encoding);//只能解决POST请求参数的中文问题
		response.setCharacterEncoding(encoding);//输出流编码
		response.setContentType("text/html;charset="+encoding);//输出流编码,通知了客户端应该使用的编码
		
		EncodingHttpServletRequest erequest = new EncodingHttpServletRequest(request);
		
		chain.doFilter(erequest, response);
	}

	public void destroy() {

	}

}
class EncodingHttpServletRequest extends HttpServletRequestWrapper{
	public EncodingHttpServletRequest(HttpServletRequest request){
		super(request);
	}

	public String getParameter(String name) {
		String value = super.getParameter(name);
		if(value==null)
			return value;
		//只管get方式
		if("get".equalsIgnoreCase(super.getMethod())){
			try {
				value = new String(value.getBytes("ISO-8859-1"),super.getCharacterEncoding());
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		return value;
	}
	
}

2、过滤脏话过滤器

1.新建 DWHttpServletRequest 类 继承  HttpServletRequestWrapper ,

2.包装 getParameter 发方法  过滤 脏话

3.执行 内容  时 得到的是  DWHttpServletRequest 的 request 所以使用的方法是 DWHttpServletRequest的 getParameter。

public class DirtyWordsFilter implements Filter {

	public void init(FilterConfig filterConfig) throws ServletException {
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request;
		HttpServletResponse response;
		try{
			request = (HttpServletRequest)req;
			response = (HttpServletResponse)resp;
		}catch(Exception e){
			throw new RuntimeException("non-http request or response");
		}
		DWHttpServletRequest dwrequest = new DWHttpServletRequest(request);
		chain.doFilter(dwrequest, response);

	}

	public void destroy() {
	}

}
class DWHttpServletRequest extends HttpServletRequestWrapper{
	private String[] strs = {"禽兽","畜生","傻B","张新鹏"};
	public DWHttpServletRequest(HttpServletRequest request){
		super(request);
	}

	public String getParameter(String name) {
		String value = super.getParameter(name);
		if(value==null)
			return value;
		
		for(String s:strs){
			value = value.replace(s, "**");
		}
		return value;
	}
}

3、html标记过滤器

方法和前2种类似

public class HtmlFilter implements Filter {

	public void init(FilterConfig filterConfig) throws ServletException {
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request;
		HttpServletResponse response;
		try{
			request = (HttpServletRequest)req;
			response = (HttpServletResponse)resp;
		}catch(Exception e){
			throw new RuntimeException("non-http request or response");
		}
		HtmlHttpServletRequest hrequest = new HtmlHttpServletRequest(request);
		chain.doFilter(hrequest, response);

	}

	public void destroy() {
	}

}
class HtmlHttpServletRequest extends HttpServletRequestWrapper{
	public HtmlHttpServletRequest(HttpServletRequest request){
		super(request);
	}

	public String getParameter(String name) {
		String value = super.getParameter(name);
		if(value==null)
			return value;
		
		value = htmlFilter(value);//转义字符
		return value;
	}

	private String htmlFilter(String message) {
		if (message == null)
            return (null);

        char content[] = new char[message.length()];
        message.getChars(0, message.length(), content, 0);
        StringBuffer result = new StringBuffer(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
            case '<':
                result.append("<");
                break;
            case '>':
                result.append(">");
                break;
            case '&':
                result.append("&");
                break;
            case '"':
                result.append(""");
                break;
            default:
                result.append(content[i]);
            }
        }
        return (result.toString());
	}
}

 

4、//*全站压缩过滤器(有难度)

public class GzipFilter implements Filter {

	public void init(FilterConfig filterConfig) throws ServletException {

	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request;
		HttpServletResponse response;
		try{
			request = (HttpServletRequest)req;
			response = (HttpServletResponse)resp;
		}catch(Exception e){
			throw new RuntimeException("non-http request or response");
		}
		GzipHttpServletResponse gresponse = new GzipHttpServletResponse(response);
		chain.doFilter(request, gresponse);
		//目标资源执行完毕后执行:要压缩
		byte b[] = gresponse.getBytes();//得到原始的数据为编码的关键点
		System.out.println("压缩前大小:"+b.length);
		//判断客户是否支持gzip压缩
		String acceptEncoding = request.getHeader("Accept-Encoding");
		if(acceptEncoding!=null&&acceptEncoding.contains("gzip")){
			//支持
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			GZIPOutputStream gout = new GZIPOutputStream(out);
			gout.write(b);
			gout.close();
			
			b = out.toByteArray();//压缩后的数据
			System.out.println("压缩后大小:"+b.length);
			//告知浏览器压缩方式
			response.setHeader("Content-Encoding", "gzip");
			response.setContentLength(b.length);//告知客户端,正文的长度
		}
		response.getOutputStream().write(b);
	}

	public void destroy() {

	}

}
class GzipHttpServletResponse extends HttpServletResponseWrapper{
	private ByteArrayOutputStream baos = new ByteArrayOutputStream();//存放截获的数据
	
	private PrintWriter pw = null;
	public GzipHttpServletResponse(HttpServletResponse response){
		super(response);
	}
	//截获输出的数据:放到baos中
	public ServletOutputStream getOutputStream() throws IOException {
		return new MyServletOutputStream(baos);
	}
	
	//字符流:截获放到baos中
	public PrintWriter getWriter() throws IOException {
		pw = new PrintWriter(new OutputStreamWriter(baos, super.getCharacterEncoding()));
		return pw;
	}
	//获取截获的数据
	public byte[] getBytes(){
		try {
			if(pw!=null){
				pw.close();
			}
			baos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return baos.toByteArray();
	}
}
class MyServletOutputStream extends ServletOutputStream{
	private ByteArrayOutputStream baos;
	public MyServletOutputStream(ByteArrayOutputStream baos){
		this.baos = baos;
	}
	public void write(int b) throws IOException {
		baos.write(b);
	}
	
}

 

 

posted @ 2016-03-26 11:28  loveincode  阅读(1315)  评论(0编辑  收藏  举报
最简单即最美
有了信仰,自己要坚持努力 2017.07.09 21:34