1). Filter 是什么 ?

Filter也称之为过滤器,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。



2)Filter的作用

在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。
在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。



3). Filter的使用方法


4). 拦截不同的方式的访问请求

在web.xml文件中,一个<filter-mapping>元素用于配置一个Filter所负责拦截的资源,<filter-mapping>元素中有一个特殊的子元素<dispatcher>,该元素用于指定过滤器所拦截的资源Servlet容器调用的方式,<dispatcher>元素的值共有4个,分别为REQUEST,INCLUDE,FORWARD和ERROR,默认REQUEST. 可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截

. REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。通过 GET 或 POST 请求直接访问。 

 FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用。或 <jsp:forward page="/..." /> 或 通过 page 指令的 errorPage 转发页面. <%@ page errorPage="test.jsp" %>

 INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。或 <jsp:include file="/..." />

 ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。



5). Filter链

在一个web应用程序中可以注册多个Filter程序,每个Filter程序都可以针对某一个URL进行拦截,如果多个Filter程序都对同一个URL进行拦截,那么这些Filter就会组成一个Filter链,Filter、链用FilterChain对象来表示,FilterChain对象中有一个doChain()方法,该方法的作用就是让Filter链上的当前过滤器放行,请求进入下一个Filter。

Filter01

public class Filter01 implements Filter {

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

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("Filter01 before...");
		chain.doFilter(request, response);
		System.out.println("Filter01 after...");
	}

	public void init(FilterConfig fConfig) throws ServletException {
		// TODO Auto-generated method stub
	}

}

Filter02

public class Filter02 implements Filter {

	public void destroy() {
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("Filter02 before...");
		chain.doFilter(request, response);
		System.out.println("Filter02 after...");
	}

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

MyServlet.java

public class MyServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
   
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("MyServlet");
	}

	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<filter>
		<filter-name>filter01</filter-name>
		<filter-class>filter.Filter01</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>filter01</filter-name>
		<url-pattern>/MyServlet</url-pattern>
	</filter-mapping>
	
	<filter>
		<filter-name>filter02</filter-name>
		<filter-class>filter.Filter02</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>filter02</filter-name>
		<url-pattern>/MyServlet</url-pattern>
	</filter-mapping>
	
	<servlet>
		<servlet-name>MyServlet</servlet-name>
		<servlet-class>servlet.MyServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>MyServlet</servlet-name>
		<url-pattern>/MyServlet</url-pattern>
	</servlet-mapping>
</web-app>

浏览器访问http://localhost:8080/Filter1/MyServlet

java filter用法 javaee filter_java

注意1:分析上面执行的顺序。

注意2:Filter链中各个Filter的拦截顺序与它们在web.xml文件中<filter-mapping>元素的映射顺序一致。



6). FilterConfig接口

1、与普通的Servlet程序一样,Filter程序也很可能需要访问Servlet容器。Servlet规范将代表ServletContext对象和Filter的配置参数信息都封装到一个称为FilterConfig的对象中。

2、FilterConfig接口则用于定义FilterConfig对象应该对外提供的方法,以便在Filter程序中可以调用这些方法来获取ServletContext对象,以及获取在web.xml文件中为Filter设置的友好名称和初始化参数。

3、FilterConfig接口定义的各个方法:

  • getFilterName方法,返回<filter-name>元素的设置值。
  • getServletContext方法,返回FilterConfig对象中所包装的ServletContext对象的引用。
  • getInitParameter方法,用于返回在web.xml文件中为Filter所设置的某个名称的初始化的参数值。
  • getInitParameterNames方法,返回一个Enumeration集合对象。

4、举个栗子

下面以getInitParameterNames(String name)方法为例,通过一个案例来演示FilterConfig接口的作用。

Filter01

public class Filter01 implements Filter {

	private String characterEncoding;
	FilterConfig config;

	public void destroy() {
	}


	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		characterEncoding = config.getInitParameter("encoding");
		System.out.println("Encoding初始化参数为:"+characterEncoding);
		chain.doFilter(request, response);
	}

	
	public void init(FilterConfig fConfig) throws ServletException {
	    //获取FilterConfig
		this.config = fConfig;
	}

}

MyServlet

@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
		response.getWriter().append("MyServlet");
	}


	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:web="http://xmlns.jcp.org/xml/ns/javaee">
  <filter>
    <filter-name>Filter01</filter-name>
    <filter-class>com.lin.filter.Filter01</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>Filter01</filter-name>
    <url-pattern>/MyServlet</url-pattern>
  </filter-mapping>
</web-app>

java filter用法 javaee filter_java_02



7)Filter实现用户自动登录

在前面了解到Cookie可以实现自动登录的功能,当用户第一次访问服务器时,服务器会发送一个包含用户信息的Cookie,之后当客户端再次访问服务器时,都会向服务器回送Cookie,这样服务器就会从Cookie中获取用户信息,从而实现用户的自动登录。

但是这里有个问题,当客户端访问服务器的Servlet时,所有的Servlet程序中都需要对用户的Cookie信息进行校验,这样势必会在Servlet中书写大量重复的程序。

为了解决上面问题,可以在Filter程序中实现Cookie的校验,由于Filter可以对服务器所有的请求进行拦截,因此一旦请求通过Filter程序,就相当于用户信息校验通过,Servlet程序根据获取到的用户信息,就可以实现自动登录了。

User.java程序

用于封装用户的信息

package cn.lin.entity;

public class User {

	private String username;
	
	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

②login.jsp页面

用于创建一个用户登录的表单,这个表单需要填写用户名与密码,以及用户自动登录的时间

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户登录</title>
</head>
<body style="text-align: center;">
	<form action="${pageContext.request.contextPath}/LoginServlet"
		method="post">
		${errorMsg }
		<table border="1" width="600px" cellpadding="0" cellspacing="0"
			align="center">
			<tr>
				<td height="30" align="center">用户名</td>
				<td><input type="text" name="username" /></td>
			</tr>
			<tr>
				<td height="30" align="center">密码</td>
				<td><input type="password" name="password" /></td>
			</tr>
			<tr>
				<td headers="35" align="center">自动登录时间</td>
				<td><input type="radio" name="autologin" value="${60*60*24*31}" />一个月
					<input type="radio" name="autologin" value="${60*60*24*31*3}" />三个月
					<input type="radio" name="autologin" value="${60*60*24*31*6}" />半年
					<input type="radio" name="autologin" value="${60*60*24*31*12}" />一年
				</td>
			</tr>
			<tr>
				<td height="30" colspan="2" align="center"><input type="submit"
					value="登录" /> <input type="reset" value="重置" /></td>
				<td></td>
			</tr>
			
		</table>
	</form>
</body>
</html>

③index.jsp页面

用于显示用户的登录信息

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>显示登陆的用户信息</title>
</head>
<body>
	<br>
	<center>
		<h3>欢迎光临</h3>
	</center>
	<br>
	<br>
	<c:choose>
		<c:when test="${sessionScope.user==null}">
			<a href="${pageContext.request.contextPath}/login.jsp">用户登录</a>
		</c:when>
		<c:otherwise>
                               欢迎您,${sessionScope.user.username}!
          <a href="${pageContext.request.contextPath}/LoginoutServlet">注销</a>
        </c:otherwise>
	</c:choose>
	<hr>
</body>
</html>

④LoginServlet程序

用于处理用户·的登录请求,如果输入的用户名和密码正确,则发送一个用户自动登录的Cookie,并跳转到首页,否则会提示错误,并跳转到login.jsp让用户重新登录。

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		if("root".equals(username)&&"admin".equals(password)){
			User user = new User();
			user.setUsername(username);
		    user.setPassword(password);
		    request.getSession().setAttribute("user", user);
		    
		    String autoLogin = request.getParameter("autoLogin");
		    if(autoLogin!=null)
		    {
		    	Cookie cookie = new Cookie("autologin", username+"-"+password);
		    	cookie.setMaxAge(Integer.parseInt(autoLogin));
		    	cookie.setPath(request.getContextPath());
		    	response.addCookie(cookie);
		    }
		    response.sendRedirect(request.getContextPath()+"/index.jsp");
		    
		}else{
			request.setAttribute("errorMsg", "用户名或密码错误");
			request.getRequestDispatcher("/login.jsp").forward(request, response);
		}
	}

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

}

⑤LoginoutServlet程序

     用于注销用户登录的信息,在这个程序中首先会将Session会话中保存的User对象删除,然后将自动登录的Cookie删除,最后跳转到index.jsp.

@WebServlet("/LoginoutServlet")
public class LoginoutServlet extends HttpServlet {
      

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.getSession().removeAttribute("user");
		
		Cookie cookie = new Cookie("autoLogin", "msg");
		cookie.setPath(request.getContextPath());
		cookie.setMaxAge(0);
		response.addCookie(cookie);
		response.sendRedirect(request.getContextPath()+"/index.jsp");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

⑥Filter01程序

用于拦截用户登录的访问请求,判断请求中是否包含用户自动登录的Cookie,如果包含则获取Cookie中的用户名和密码,并验证用户名与密码是否正确,如果正确,则将用户的登录信息封装到User对象存入到Session域中,完成用户自动登录。

public class Filter01 implements Filter {

	private String characterEncoding;
	FilterConfig config;

	public void destroy() {
	}

	public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		Cookie[] cookie = request.getCookies();
		String autologin = null;
		for (int i = 0; cookie != null && i < cookie.length; i++) {
			if ("autologin".equals(cookie[i].getName())) {

				autologin = cookie[i].getValue();
				break;
			}
		}
		if (autologin != null) {
			String[] parts = autologin.split("-");
			String username = parts[0];
			String password = parts[1];
			if ("root".equals(username) && "admin".equals(password)) {
				User user = new User();
				user.setUsername(username);
				user.setPassword(password);
				request.getSession().setAttribute("user", user);
			}
		}
		chain.doFilter(request, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
		// 获取FilterConfig
		this.config = fConfig;
	}

}

java filter用法 javaee filter_web.xml_03

java filter用法 javaee filter_java_04

java filter用法 javaee filter_web.xml_05



8)Filter解决编码问题

我们平时设置编码的方法有:

1、在JSP页面设置

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

2、在Serlvet中设置



request.setCharacterEncoding("UTF-8");
		
response.setCharacterEncoding("UTF-8");

或者是:

request.setCharacterEncoding("UTF-8");
		
response.setContentType("text/html;charset=UTF-8");

然而,上面这两种是我们最常用的方式,但是在大型项目中,我们写这么多解决字符编码集的代码,是不是所谓“重复性的垃圾代码”?所以,我们最好的解决方法是在项目中写一个处理字符编码的过滤器。

public class CharsetEncodingFilter implements Filter {  
	
	private static String encoding; // 定义变量接收初始化的值
 
	public void destroy() {
		
	}
 
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// 设置字符编码链锁
		request.setCharacterEncoding(encoding);
		response.setCharacterEncoding(encoding);
		chain.doFilter(request, response);
		
	}
	// 初始化
	public void init(FilterConfig config) throws ServletException {
		// 接收web.xml配置文件中的初始参数
		encoding = config.getInitParameter("CharsetEncoding");
		
	}
 
}

web.xml配置文件中的代码:

<filter>
  	<filter-name>charsetEncodingFilter</filter-name>
  	<filter-class>cn.lin.filter.CharsetEncodingFilter</filter-class>
  	<init-param>
  		<param-name>CharsetEncoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </filter>
  
  <filter-mapping>
  	<filter-name>charsetEncodingFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>