JavaWeb -- Servlet Filter 过滤器

1. Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,如下所示:

JavaWeb -- Servlet Filter 过滤器_第1张图片

Filter 开发分为二个步骤:
编写 java 类实现 Filter 接口,并实现其 doFilter 方法
web.xml文件中使用元素对编写的filter 类进行注册,并设置它所能拦截的资源。(动手实验)
Filter链
在一个 web 应用中,可以开发编写多个 Filter ,这些 Filter 组合起来称之为一个 Filter 链。
web 服务器根据 Filter web.xml 文件中的注册顺序,决定先调用哪个 Filter ,当第一个 Filter doFilter 方法被调用时, web 服务器会创建一个代表 Filter 链的 FilterChain 对象传递给该方法。在 doFilter 方法中,开发人员如果调用了 FilterChain 对象的 doFilter 方法,则 web 服务器会检查 FilterChain 对象中是否还有 filter ,如果有,则调用第 2 filter ,如果没有,则调用目标资源。
Filter的生命周期
init(FilterConfig filterConfig)throws ServletException
和我们编写的 Servlet 程序一样, Filter 的创建和销毁由 WEB 服务器负责 web 应用程序 启动时 web 服务器将 创建 Filter 的实例对象 ,并调用其 init 方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(注: filter 对象只会创建一次, init 方法也只会执行一次。 示例 )
开发人员通过 init 方法的参数,可获得代表当前 filter 配置信息的 FilterConfig 对象。 (filterConfig 对象见下页 PPT)
destroy ()
Web容器卸载Filter 对象之前被调用。该方法在 Filter 的生命周期中仅执行一次。 在这个方法中,可以释放过滤器使用的资源。
FilterConfig接口
用户在配置 filter 时,可以使用 filter 配置一些初始化参数,当 web 容器实例化 Filter 对象,调用其 init 方法时,会把封装了 filter 初始化参数的 filterConfig 对象传递进来。因此开发人员在编写 filter 时,通过 filterConfig 对象的方法,就可获得:
String getFilterName() :得到 filter 的名称。
String getInitParameter(String name) :返回在部署描述中指定名称的初始化参数的值。如果不存在返回 null.
Enumeration getInitParameterNames() :返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext() :返回 Servlet 上下文对象的引用。
Filter的部署
用于为过滤器指定一个名字,该元素的内容不能为空。
元素用于指定过滤器的完整的限定类名。
元素用于为过滤器指定初始化参数,它的子元素 指定参数的名字, 指定参数的值。在过滤器中,可以使用 FilterConfig 接口对象来访问初始化参数。
元素用于设置一个 Filter 所负责拦截的资源。一个 Filter 拦截的资源可通过两种方式来指定: Servlet 名称和资源访问的请求路径
子元素用于设置 filter 的注册名称。该值必须是在 元素中声明过的过滤器的名字
设置 filter 所拦截的请求路径 ( 过滤器关联的 URL 样式 )
指定过滤器所拦截的 Servlet 名称。
指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是 REQUEST,INCLUDE,FORWARD ERROR 之一,默认 REQUEST 。用户可以设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
子元素可以设置的值及其意义:
REQUEST :当用户直接访问页面时, Web 容器将会调用过滤器。如果目标资源是通过 RequestDispatcher include() forward() 方法访问时,那么该过滤器就不会被调用。
INCLUDE :如果目标资源是通过 RequestDispatcher include() 方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD :如果目标资源是通过 RequestDispatcher forward() 方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR :如果目标资源是通过 声明式异常处理机制 调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。

 	     testFitler
	     org.test.TestFiter
	     
		 word_file	
		 /WEB-INF/word.txt
	     
 

     testFilter
    /test.jsp


    testFilter
   /index.jsp
   REQUEST
   FORWARD

2. 实例
Filter常见应用(1) -- 全站乱码过滤
统一全站字符编码的过滤器
通过配置参数 encoding 指明使用何种字符编码 , 以处理 Html Form 请求参数的中文问题
过滤器代码:
public class EncodingFilter implements Filter {

	private FilterConfig filterConfig;
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		
		String charset = this.filterConfig.getInitParameter("charset");
		request.setCharacterEncoding(charset);
		response.setCharacterEncoding(charset);
		response.setContentType("text/html;charset="+ charset);
		
		MyCharacterEncodingRequest myrequest = new MyCharacterEncodingRequest(request);
		
		chain.doFilter(myrequest, response);
	}

	@Override
	public void destroy() {
	}
}

/*
 * 装饰者模式
1.实现与被增强对象相同的接口 
2、定义一个变量记住被增强对象
3、定义一个构造器,接收被增强对象
4、覆盖需要增强的方法
5、对于不想增强的方法,直接调用被增强对象(目标对象)的方法
 */

class MyCharacterEncodingRequest extends HttpServletRequestWrapper
{
	private HttpServletRequest request = null;
	
	public MyCharacterEncodingRequest(HttpServletRequest request) {
		super(request);
		this.request = request;
	}

	@Override
	public String getParameter(String name) {
		
		try
		{
			String value = this.request.getParameter(name);		
			if(value==null)
				return null;		
			if(request.getMethod().equalsIgnoreCase("post"))
				return value;		
			value = new String( value.getBytes("ISO8859-1"), this.request.getCharacterEncoding());		
			return value;
		}
		catch(Exception e)
		{
			throw new RuntimeException(e);
		}
	}
		
web.xml 配置

  	EncodingFilter
  	com.kevin.web.EncodingFilter
  	
  		charset
  		UTF-8
  	
  
  
  
  	EncodingFilter
  	/*  
  
测试servlet
public class servlet1 extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public servlet1() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().write("servlet1中国");
	 	String param1 = request.getParameter("param1");
	 	String param2 = request.getParameter("param2");
	 	System.out.println("Param1: " + param1);
	 	System.out.println("Param2: " + param2);
		
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request, response);
	}

}
测试jsp
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>   




 


	
	
		
	
	encodingTest get方式测试
	
	
	
输入 提交

Filter常见应用(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 秒。
过滤器代码:
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);				
	}

	public void init(FilterConfig filterConfig) throws ServletException {	
	}
	
	public void destroy() {
	}
}
web.xml配置: 拦截所需资源
 
Filter常见应用(3) -- 控制缓存
控制浏览器缓存页面中的静态资源的过滤器:
场景:有些动态页面中引用了一些图片或 css 文件以修饰页面效果,这些图片和 css 文件经常是不变化的,所以为减轻服务器的压力,可以使用 filter 控制浏览器缓存这些文件,以提升服务器的性能。
过滤器代码:
public class ExpiresFilter implements Filter {

	private FilterConfig filterConfig;
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
	}

	@Override
	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.得到用户想访问的资源的后缀名
		String ext = uri.substring( uri.lastIndexOf(".")+1 );
				
		//3.得到资源需要缓存的时间
		String time = filterConfig.getInitParameter(ext);
		if(time!=null)
		{
			long t = Long.parseLong(time) * 3600 * 1000;
			response.setDateHeader("expires", System.currentTimeMillis() + t);
		}
		chain.doFilter(request, response);
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub

	}

}
web.xml配置方法

  	ExpiresFilter
  	com.kevin.web.ExpiresFilter
  	
  		css
  		4
  	
  	
  		jpg
  		1
  	
  	
  		js
  		4
  	
  
  
  
  	ExpiresFilter
  	*.css
  
  
  
  	ExpiresFilter
  	*.jpg
  
  
  
  	ExpiresFilter
  	*.js
  

Filter常见应用(4)
使用 Filter 实现 URL 级别的权限认证
情景:在实际开发中我们经常把一些执行敏感操作的 servlet 映射到一些特殊目录中,并用 filter 把这些特殊目录保护起来,限制只能拥有相应访问权限的用户才能访问这些目录下的资源。从而在我们系统中实现一种 URL 级别的权限功能。
要求:为使 Filter 具有通用性, Filter 保护的资源和相应的访问权限通过 filter 参数的形式予以配置。
 
Filter常见应用(5)
实现用户自动登陆的过滤器
在用户登陆成功后,发送一个名称为 user cookie 给客户端, cookie 的值为用户名和 md5 加密后的密码。
编写一个 AutoLoginFilter ,这个 filter 检查用户是否带有名称为 user cookie 来,如果有,则调用 dao 查询 cookie 的用户名和密码是否和数据库匹配,匹配则向 session 中存入 user 对象(即用户登陆标记),以实现程序完成自动登陆
过滤器代码:
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;
		
		if(request.getSession().getAttribute("user")!=null){
			chain.doFilter(request, response);
			return;
		}
		
		
		//1.得到用户带过来的authlogin的cookie,
		String value = null;
		Cookie cookies[] = request.getCookies();
		for(int i=0;cookies!=null && i
登录servlet
public class LoginServlet extends HttpServlet {

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

		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		UserDao dao = new UserDao();
		User user = dao.find(username, password);
		if(user==null){
			request.setAttribute("message", "用户名或密码不对!!");
			request.getRequestDispatcher("/message.jsp").forward(request, response);
			return;
		}
		
		request.getSession().setAttribute("user", user);
		request.setAttribute("message", "恭喜,登陆成功!!");
		
		//发送自动登陆cookie
		sendAutoLoginCookie(request,response,user);
		request.getRequestDispatcher("/message.jsp").forward(request, response);
	}

	private void sendAutoLoginCookie(HttpServletRequest request, HttpServletResponse response, User user) {
		
		int logintime = Integer.parseInt(request.getParameter("logintime"));
		Cookie cookie = new Cookie("autologin",user.getUsername() + "." + WebUtils.md5(user.getPassword()));
		cookie.setMaxAge(logintime);
		cookie.setPath("/day18");
		response.addCookie(cookie);
	}
		
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}
}
 
3. request对象的增强
Servlet API 中提供了一个 request 对象的 Decorator 设计模式的默认实现类 HttpServletRequestWrapper ,( HttpServletRequestWrapper 类实现了 request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法)以避免用户在对 request 对象进行增强时需要实现 request 接口中的所有方法。
上面的乱码过滤就是request增强。
------------------- 脏话 过滤器 --------------------------
//脏话过滤器
public class DirtyFilter implements Filter {
	
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {

		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		DirtyRequest dirtyrequest = new DirtyRequest(request);
		
		chain.doFilter(dirtyrequest, response);
	}

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

class DirtyRequest extends HttpServletRequestWrapper{

	private List dirtyWords = Arrays.asList("傻B","操蛋","畜生");
	private HttpServletRequest request;
	public DirtyRequest(HttpServletRequest request) {
		super(request);
		this.request = request;
	}
	@Override
	public String getParameter(String name) {
		
		String value = this.request.getParameter(name);
		if(value==null){
			return null;
		}
		
		for(String dirtyWord : dirtyWords){
			if(value.contains(dirtyWord)){
				value = value.replace(dirtyWord, "****");
			}
		}
		return value;
	}
}

------------------------  HTML 过滤器 ------------------------------
//html转义过滤器
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;

		MyHtmlRequest myrequest = new MyHtmlRequest(request);
		chain.doFilter(myrequest, response);	
	}
	
	public void destroy() {
		// TODO Auto-generated method stub
	}
	
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
	}
}

class MyHtmlRequest extends HttpServletRequestWrapper{

	private HttpServletRequest request;
	
	public MyHtmlRequest(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());

	    }	
}

4. response对象的增强
Servlet  API 中提供了 response 对象的 Decorator 设计模式的默认实现类 HttpServletResponseWrapper ,( HttpServletResponseWrapper 类实现了 response 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 response 对象的对应方法)以避免用户在对 response 对象进行增强时需要实现 response 接口中的所有方法。
//解决全站压缩
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;
		
		BufferResponse myresponse = new BufferResponse(response);
		
		chain.doFilter(request, myresponse);
		
		
		//拿出缓存中的数据,压缩后再打给浏览器
		byte out[] = myresponse.getBuffer();
		System.out.println("原始大小:" + out.length);
		
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		GZIPOutputStream gout = new GZIPOutputStream(bout);
		gout.write(out);
		gout.close();
		
		
		byte gzip[] = bout.toByteArray();
		System.out.println("压缩后的大小:" + gzip.length);
		
		
		response.setHeader("content-encoding", "gzip");
		response.setContentLength(gzip.length);
		response.getOutputStream().write(gzip);
	}
	
	public void destroy() {
	}

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

class BufferResponse extends HttpServletResponseWrapper{

	private ByteArrayOutputStream bout = new ByteArrayOutputStream();
	
	private PrintWriter pw;
	private HttpServletResponse response;
	public BufferResponse(HttpServletResponse response) {
		super(response);
		this.response = response;
	}
	@Override
	public ServletOutputStream getOutputStream() throws IOException {
		return new MyServletOutputStream(bout);
	}
	@Override
	public PrintWriter getWriter() throws IOException {
		pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding()));  //PrintWriter.write(中国) 
return pw; } public byte[] getBuffer(){ try{ if(pw!=null){ pw.close(); } if(bout!=null){ bout.flush(); return bout.toByteArray(); } return null; }catch (Exception e) { throw new RuntimeException(e); } } } class MyServletOutputStream extends ServletOutputStream{ private ByteArrayOutputStream bout; public MyServletOutputStream(ByteArrayOutputStream bout){ this.bout = bout; } @Override public void write(int b) throws IOException { this.bout.write(b); } }

-------------------------------  案例: 缓存数据到内存 -------------------------------
过滤器
//缓存数据到内存
public class CachedFilter implements Filter {

	private Map map = new HashMap();
	

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		
		//1.得到用户请求的uri
		String uri = request.getRequestURI();
		
		//2.看缓存中有没有uri对应的数据
		byte b[] = map.get(uri);
		
		//3.如果缓存中有,直接拿缓存的数据打给浏览器,程序返回
		if(b!=null){
			response.getOutputStream().write(b);
			return;
		}
		
		//4.如果缓存没有,让目标资源执行,并捕获目标资源的输出
		BufferResponse1 myresponse = new BufferResponse1(response);
		chain.doFilter(request, myresponse);
		byte out[] = myresponse.getBuffer();
		
		//5.把资源的数据以用户请求的uri为关键字保存到缓存中
		map.put(uri, out);
		
		//6.把数据打给浏览器
		response.getOutputStream().write(out);
	}

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

class BufferResponse1 extends HttpServletResponseWrapper{

	private ByteArrayOutputStream bout = new ByteArrayOutputStream();  //捕获输出的缓存
	
	private PrintWriter pw;
	
	private HttpServletResponse response;
	public BufferResponse1(HttpServletResponse response) {
		super(response);
		this.response = response;
	}
	@Override
	public ServletOutputStream getOutputStream() throws IOException {
		
		return new MyServletOutputStream1(bout);
	}
	@Override
	public PrintWriter getWriter() throws IOException {
		pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding()));
		return pw;
	}
	
	public byte[] getBuffer(){
		try{
			if(pw!=null){
				pw.close();
			}
			return bout.toByteArray();
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

class MyServletOutputStream1 extends ServletOutputStream{

	private ByteArrayOutputStream bout;
	public MyServletOutputStream1(ByteArrayOutputStream bout){  //接收数据写到哪里
		this.bout = bout;
	}
	
	@Override
	public void write(int b) throws IOException {
		bout.write(b);
	}
	
}



 

你可能感兴趣的:(JavaWeb)