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
注意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>
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;
}
}
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>