首页 > WEB开发 > 后台开发 > 过滤器(Filter)
2014
08-28

过滤器(Filter)

12. 过滤器(Filter)12

1、概述

过滤器,又叫拦截器,属于Servlet中的技术,Since Servlet2.3。使用Filter技术可以对web服务器管理的所有web资源,例如JSP、Servlet、静态资源等进行拦截,从而实现一些特殊的功能(后面会有八个案例)。

12. 过滤器(Filter)138

2、快速入门

1)过滤器编写步骤

① 在Web工程中新建Java类实现Filter接口,并实现doFilter()方法【可打印一句话,来证明能够进行拦截】

public class FilterDemo1 implements Filter {
	// 应用被加载时完成过滤器的实例化
	public FilterDemo1() {
		System.out.println("调用了默认构造方法");
	}

	// 初始化:服务器传入FilterConfig参数
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("调用初始化方法");
	}

	// 用户每次访问被过滤资源都会调用过滤器的doFilter方法
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("FilterDemo1第一次拦截");
		// 对request、response进行预处理,代码放在chain.doFilter()前
		chain.doFilter(request, response);// 放行
		// 对响应信息进行拦截,代码放在chain.doFilter()后
		System.out.println("FilterDemo1第二次拦截");
	}

	// 应用从服务器上卸载时调用销毁方法
	public void destroy() {
		System.out.println("调用销毁方法");
	}
}

② 在web.xml中配置Filter(filter、filter-mapping元素),指定拦截范围【类似Servlet配置,配置完成后,访问一个页面,看看拦截效果】:

<filter>
	<filter-name>FilterDemo1</filter-name>
	<filter-class>org.flyne.filter.FilterDemo1</filter-class>
</filter>

<filter-mapping>
	<filter-name>FilterDemo1</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

/* 表示过滤器可以过滤用户请求的任意web资源。当用户访问一个页面时,就会被FilterDemo1拦截,并打印”FilterDemo1第一次拦截”,返回时,会再次经过该Filter,打印”FilterDemo1第二次拦截”。执行过程见概述中的图。

2)doFilter()方法

WEB服务器每次在调用web资源之前,都会先调用doFilter方法,因此,在该方法内编写代码可达到如下目的:

① 调用目标资源之前,对request、response进行预处理

② 是否调用目标资源,即是否让用户访问web资源

Web服务器在调用doFilter方法时,会传递一个filterChain对象进来,它也提供了一个doFilter方法,我们可以根据需求决定是否调用此方法,调用该方法,则web资源就会被访问(放行),否则web资源不会被访问(一直处于拦截状态)。

③ 调用目标资源之后,对响应信息进行过滤

3)Filter链(FilterChain)

① 在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链

② Web服务器根据Filter在web.xml文件中的注册顺序(按filter-mapping元素的顺序),决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,Web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。

在doFilter方法中,如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

3、一些细节问题

1)Filter生命周期

  • 诞生(init):过滤器的实例是在应用被加载时就完成的实例化,并初始化的。
  • 存活(doFilter):和应用的生命周期一致的。针对拦截范围内的资源访问,每次访问都会调用void doFIlter(request,response.chain)进行拦截。
  • 死亡(destroy):应用被卸载(undeploy)。

如上面的FilterDemo1,当部署(Deploy)该应用到服务器时,就会产生FilterDemo1的实例,并调用初始化方法;最后将该应用从服务器卸载(undeploy)时,调用destroy方法。

2)FilterConfig接口【类似ServletConfig】

在配置Filter时,可以使用为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。filterConfig中的方法如下:

  • String getFilterName():得到Filter名称
  • String getInitParameter(name):返回中配置的参数,不存在返回null

3)子元素的配置

在配置拦截范围()时,可以配置多个子元素,使得Filter将会拦截客户端直接请求的资源(REQUEST)、请求转发的目标资源(FORWARD)、包含的目标资源(INCLUDE)和中指定的目标资源(ERROR)。默认值为REQUEST。

4、基础案例(4个)

1)SetCharacterEncodingFilter:解决POST请求参数和响应输出的中文乱码

public class SetCharacterEncodingFilter implements Filter {

	private String encoding;//编码可以通过<init-param>元素配置

	public void init(FilterConfig filterConfig) throws ServletException {
		encoding = filterConfig.getInitParameter("encoding");
		if(encoding == null){//如果用户忘记配置,默认encoding为UTF-8
			encoding = "UTF-8";
		}
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		//只能解决POST请求参数乱码问题
		request.setCharacterEncoding(encoding);
		//指定输出编码(最后带上,后面会有说明)
		response.setCharacterEncoding(encoding);
		//指定输出流编码及客户端应使用的码表
		response.setContentType("text/html;charset="+encoding);
		chain.doFilter(request, response);
	}

	public void destroy() {

	}
}
------------------------web.xml配置------------------------
<filter>
	<filter-name>SetCharacterEncodingFilter</filter-name>
	<filter-class>org.flyne.examples.SetCharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>SetCharacterEncodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

2)NoCacheFilter:禁止客户端缓存动态资源

public void doFilter(ServletRequest req, ServletResponse resp,
		FilterChain chain) throws IOException, ServletException {
	//响应头为HTTP协议里的,需转换一下
	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", "0");
	response.setHeader("Cache-Control", "no-cache");
	response.setHeader("Pragma", "no-cache");
	chain.doFilter(request, response);
}
------------------------web.xml配置------------------------
<filter>
	<filter-name>NoCacheFilter</filter-name>
	<filter-class>org.flyne.examples.NoCacheFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>NoCacheFilter</filter-name>
	<url-pattern>/servlet/*</url-pattern>
	<url-pattern>*.jsp</url-pattern>
</filter-mapping>

3)SetCacheExpiresFilter:控制静态资源缓存时间

public class SetCacheExpiresFilter implements Filter {

	private int htmlExp;//HTML的缓存时间,单位为小时,下同
	private int cssExp;//CSS的缓存时间
	private int jsExp;//JS的缓存时间

	public void init(FilterConfig filterConfig) throws ServletException {
		htmlExp = Integer.parseInt(filterConfig.getInitParameter("html"));
		cssExp = Integer.parseInt(filterConfig.getInitParameter("css"));
		jsExp = Integer.parseInt(filterConfig.getInitParameter("js"));
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// 响应头为HTTP协议里的,需转换一下
		HttpServletRequest request = null;
		HttpServletResponse response = null;
		try {
			request = (HttpServletRequest) req;
			response = (HttpServletResponse) resp;
		} catch (Exception e) {
			throw new RuntimeException("not-http request or response");
		}

		//根据请求资源的后缀名确定缓存时间
		String uri = request.getRequestURI();
		String extName = uri.substring(uri.lastIndexOf(".")+1);
		long expTime = 0;
		if("html".equals(extName)){
			expTime = System.currentTimeMillis()+htmlExp*60*60*1000;

		}else if("css".equals(extName)){
			expTime = System.currentTimeMillis()+cssExp*60*60*1000;
		}else if("js".equals(extName)){
			expTime = System.currentTimeMillis()+jsExp*60*60*1000;
		}
		response.setDateHeader("Expires", expTime);

		chain.doFilter(request, response);
	}

	public void destroy() {
	}
}
------------------------web.xml配置------------------------
<filter>
	<filter-name>SetCacheExpiresFilter</filter-name>
	<filter-class>org.flyne.examples.SetCacheExpiresFilter</filter-class>
	<init-param>
		<param-name>html</param-name>
		<param-value>1</param-value>
	</init-param>
	<init-param>
		<param-name>css</param-name>
		<param-value>2</param-value>
	</init-param>
	<init-param>
		<param-name>js</param-name>
		<param-value>3</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>SetCacheExpiresFilter</filter-name>
	<url-pattern>*.html</url-pattern>
	<url-pattern>*.css</url-pattern>
	<url-pattern>*.js</url-pattern>
</filter-mapping>

4)AutoLoginFilter :用户自动登录

当用户登陆时,将表单提交到LoginServlet处理,如果用户勾选了记住我,则将用户的登陆信息存入Cookie:Cookie名为“logInfo”,值为(用户名的base64加密结果_密码的md5加密结果)。下面的自动登录过滤器基于以上信息:

public class AutoLoginFilter implements Filter {

	//表现层同service层打交道
	private UserService service = new UserServiceImpl();

	public void init(FilterConfig filterConfig) throws ServletException {
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// 转为Http协议的request和response
		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) {
			Cookie[] cookies = request.getCookies();
			for (int i = 0; cookies != null && i < cookies.length; i++) {
				Cookie cookie = cookies[i];
				//名为logInfo的Cookie记录了登录信息(用户名、密码)
				if ("logInfo".equals(cookie.getName())) {
					String value = cookie.getValue();
					//Cookie中的用户名是经过Base64加密的,所以需要解密
					String username = SecurityUtil.base64Decode(value.split("_")[0]);
					String password = value.split("_")[1];
					//Cookie中的密码是md5加密后的,所以第三个参数为true
					User user = service.login(username, password, true);
					//通过则在session中设置登陆标记
					if(user!=null){
						session.setAttribute("user", user);
					}
					break;
				}
			}
		}
		chain.doFilter(request, response);
	}

	public void destroy() {
	}
}

留下一个回复

你的email不会被公开。