Servlet之过滤器

1,Filter的生命周期
和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(注:filter对象只会创建一次,init方法也只会执行一次。)。在Web容器卸载 Filter 对象之前调用destroy方法。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。


2,FilterConfig(类比ServletConfig)
用户在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
•StringgetFilterName():得到filter的名称。
•StringgetInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
•EnumerationgetInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
•publicServletContext getServletContext():返回Servlet上下文对象的引用。


3,Filter部署注意点
<filter-mapping>元素用于设置一个Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
•<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
•<url-pattern>设写法和Servlet一样
•<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。

<dispatcher>子元素可以设置的值及其意义:
•REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
•INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
•FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
•ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。


4,Filter常见应用
[1]统一全站字符编码的过滤器(但是只能处理post,需要增强后处理get)
[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秒。
[3]控制浏览器缓存页面中的静态资源的过滤器:
场景:有些动态页面中引用了一些图片或css文件以修饰页面效果,这些图片和css文件经常是不变化的,所以为减轻服务器的压力,可以使用filter控制浏览器缓存这些文件,以提升服务器的性能
[4]使用Filter实现URL级别的权限认证
•情景:在实际开发中我们经常把一些执行敏感操作的servlet映射到一些特殊目录中,并用filter把这些特殊目录保护起来,限制只能拥有相应访问权限的用户才能访问这些目录下的资源。从而在我们系统中实现一种URL级别的权限功能。
[5]实现用户自动登陆的过滤器
•在用户登陆成功后,发送一个名称为user的cookie给客户端,cookie的值为用户名和md5加密后的密码。
•编写一个AutoLoginFilter,这个filter检查用户是否带有名称为user的cookie来,如果有,则调用dao查询cookie的用户名和密码是否和数据库匹配,匹配则向session中存入user对象(即用户登陆标记),以实现程序完成自动登陆。


5,Filter高级开发
由于开发人员在filter中可以得到代表用户请求和响应的request、response对象,因此在编程中可以使用Decorator(装饰器)模式对request、response对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求。

[request对象的增强]

Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper ,(HttpServletRequestWrapper 类实现了request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的request 对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。
案例:
l使用Decorator模式包装request对象,完全解决get、post请求方式下的乱码问题。
l使用Decorator模式包装request对象,实现html标签转义功能(Tomcat服务器中提供了转义html标签的工具类)。
[response对象的增强]
lServlet API 中提供了response对象的Decorator设计模式的默认实现类HttpServletResponseWrapper ,(HttpServletResponseWrapper类实现了response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的response对象的对应方法)以避免用户在对response对象进行增强时需要实现response接口中的所有方法。
案列:
l应用HttpServletResponseWrapper对象,压缩响应正文内容。

思路:通过filter向目标页面传递一个自定义的response对象。在自定义的response对象中,重写getOutputStream方法和getWriter方法,使目标资源调用此方法输出页面内容时,获得的是我们自定义的ServletOutputStream对象。在我们自定义的ServletOuputStream对象中,重写write方法,使写出的数据写出到一个buffer中。当页面完成输出后,在filter中就可得到页面写出的数据,从而我们可以调用GzipOuputStream对数据进行压缩后再写出给浏览器,以此完成响应正文件压缩功能。


6,代码片段

6.1--[真正解决全站乱码--request增强]

//真正解决全站乱码
public class CharacterEncodingFilter2 implements Filter {

	
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		
		request.setCharacterEncoding("UTF-8");  //post  get
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		
		chain.doFilter(new MyRequest(request), response);   //request.getparameter("password");
	}
	
	/*
	1.写一个类,实现与被增强对象相同的接口
	2.定义一个变量,记住被增强对象
	3.定义一个构造方法,接收被增强对象
	4.覆盖想增强的方法
	5.对于不想增强的方法,直接调用被增强对象(目标对象)的方法
	 */
	
	class MyRequest extends HttpServletRequestWrapper{

		private HttpServletRequest request;
		public MyRequest(HttpServletRequest request) {
			super(request);
			this.request = request;
		}
		@Override
		public String getParameter(String name) {
			
			String value = this.request.getParameter(name);
			if(!request.getMethod().equalsIgnoreCase("get")){
				return value;
			}
			
			if(value==null){
				return null;
			}
			
			try {
			return value = new String(value.getBytes("iso8859-1"),request.getCharacterEncoding());
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}			
		}		
	}
......
}
6.2--[禁止浏览器缓存所有动态页面的过滤器]
public class NoCacheFilter implements Filter {

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		
		response.setDateHeader("Expires",-1);
		response.setHeader("Cache-Control","no-cache");
		response.setHeader("Pragma","no-cache");
		
		chain.doFilter(request, response);

	}

6.3--[ 控制浏览器缓存页面中的静态资源的过滤器 ]
//控制浏览器缓存的过滤器
public class CacheFilter implements Filter {

	private FilterConfig config;
	
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		
		//1.获取到用户想访问的资源
		String uri = request.getRequestURI();
		
		//2.获取该资源的缓存时间
		int expires = 0;
		if(uri.endsWith(".jpg")){
			expires = Integer.parseInt(this.config.getInitParameter("jpg"));
		}else if(uri.endsWith(".css")){
			expires = Integer.parseInt(this.config.getInitParameter("css"));
		}else{
			expires = Integer.parseInt(this.config.getInitParameter("js"));
		}
		
		response.setDateHeader("expires", System.currentTimeMillis()+expires*60*1000);
		chain.doFilter(request, response);
	}
6.4--[用户自动登陆的过滤器]

import sun.misc.BASE64Encoder;

public class AutoLoginFilter implements Filter {

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

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

		// 1.先检查用户是否已登陆,没登陆才自动登陆
		User user = (User) request.getSession().getAttribute("user");
		if (user != null) {
			chain.doFilter(request, response);
			return;
		}

		// 2.没登陆,再执行自动登陆逻辑

		// 看用户有没有带自动登陆的cookie
		Cookie autoLoginCookie = null;
		Cookie cookies[] = request.getCookies();
		for (int i = 0; cookies != null && i < cookies.length; i++) {
			if (cookies[i].getName().equals("autologin")) {
				autoLoginCookie = cookies[i];
			}
		}
		if (autoLoginCookie == null) {
			chain.doFilter(request, response);
			return;
		}

		// 用户带了自动登陆的cookie,则先检查cookie的有效期
		String values[] = autoLoginCookie.getValue().split("\\:");
		if (values.length != 3) {
			chain.doFilter(request, response);
			return;
		}
		long expirestime = Long.parseLong(values[1]);
		if (System.currentTimeMillis() > expirestime) {
			chain.doFilter(request, response);
			return;
		}

		// 代表cookie时间有效,再检查cookie的有效性
		String username = values[0];
		String client_md5 = values[2];

		BusinessService service = new BusinessService();
		user = service.findUser(username);
		if (user == null) {
			chain.doFilter(request, response);
			return;
		}
		// //autologin=username:expirestime:md5(password:expirestime:username)
		String server_md5 = md5(user.getUsername(), user.getPassword(),
				expirestime);
		if (!server_md5.equals(client_md5)) {
			chain.doFilter(request, response);
			return;
		}

		// 执行登陆
		request.getSession().setAttribute("user", user);
		chain.doFilter(request, response);

	}

	private String md5(String username, String password, long expirestime) {

		try {
			String value = password + ":" + expirestime + ":" + username;
			MessageDigest md = MessageDigest.getInstance("md5");
			byte md5[] = md.digest(value.getBytes());
			BASE64Encoder encode = new BASE64Encoder();
			return encode.encode(md5);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}......
}
6.5--[压缩响应]

public class GzipFilter implements Filter {

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		MyResponse myresponse = new MyResponse(response);
		
		
		chain.doFilter(request, myresponse);  //response.getwriter  response.getOutputStream  
		
		//取出缓冲的数据压缩后输出
		byte out[] = myresponse.getBuffer();  //得到目标资源的输出
		System.out.println("压之前:" + out.length);
		
		
		byte gzipout[] = gzip(out);
		System.out.println("压之后:" + gzipout.length);

		
		response.setHeader("content-encoding", "gzip");
		response.setHeader("content-length", gzipout.length + "");
		response.getOutputStream().write(gzipout);
	}
	
	public byte[] gzip(byte b[]) throws IOException{
		
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		GZIPOutputStream gout = new GZIPOutputStream(bout);
		gout.write(b);
		gout.close();
		return bout.toByteArray();
	}
	
	class MyResponse extends HttpServletResponseWrapper{
		private ByteArrayOutputStream bout = new ByteArrayOutputStream();
		private PrintWriter pw;
		
		private HttpServletResponse response;
		public MyResponse(HttpServletResponse response) {
			super(response);
			this.response = response;
		}
		@Override
		public ServletOutputStream getOutputStream() throws IOException {
			return new MyServletOutputStream(bout);    //myresponse.getOutputStream().write("hahah");
		}
		
		@Override
		public PrintWriter getWriter() throws IOException {
			pw = new PrintWriter(new OutputStreamWriter(bout,response.getCharacterEncoding()));
			return pw;  //MyResponse.getWriter().write("中国");
		}
		public byte[] getBuffer(){
			if(pw!=null){
				pw.close();
			}
			return bout.toByteArray();
		}
	}
	
	class MyServletOutputStream extends ServletOutputStream{

		private ByteArrayOutputStream bout;
		public MyServletOutputStream(ByteArrayOutputStream bout){
			this.bout = bout;
		}
		@Override
		public void write(int b) throws IOException {
			bout.write(b);
		}
		
	}
}
6.6--[字符转义过滤]

public class HtmlFilter implements Filter {
	
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		
		chain.doFilter(new MyRequest(request), response);  //request.getParameter("resume");  //<script>
		

	}
	
	class MyRequest extends HttpServletRequestWrapper{
		private HttpServletRequest request;
		public MyRequest(HttpServletRequest request) {
			super(request);
			this.request = request;
		}
		@Override
		public String getParameter(String name) {
			
			String value = this.request.getParameter(name);
			if(value==null){
				return null;
			}
			return filter(value);
		}
		
		public String filter(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());

	    }

}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值