Filter拦截器

本篇文章主要是作者学习Filter的一些记录,希望能帮到您,如果有什么不妥当的地方希望得到指出,谢谢阅读
Java学习大纲:https://blog.csdn.net/weixin_39778570/article/details/94667501
JavaWeb:https://blog.csdn.net/weixin_39778570/article/details/97800485

Java中Servlet Filter拦截器详解

接口中包含的方法

//该方法在客户端请求及服务器端回复时都将被自动调用
public void doFilter (ServletRequest, ServletResponse, FilterChain)

//初始化配置参数,在doFilter()方法之前被调用
public void init(FilterConfig filterConfig)

//结束过滤器,doFilter()方法完成后被调用
public void destroy()

doFilter参数

ServletRequest:对于简单的过滤器,大多数过滤逻辑是基于这个对象的。
 如果处理HTTP请求,并且需要访问诸如getHeader或getCookies等在ServletRequest
 中无法得到的方法,就要把此对象构造成 HttpServletRequest。
ServletResponse:除了在两个情形下要使用它以外,通常忽略这个参数。
 首先,如果希望完全阻塞对相关 servlet或JSP页面的访问。
 可调用response.getWriter并直接发送一个响应到客户机。
 其次,如果希望修改相关的servlet或 JSP页面的输出,可把响应包含在一个收集所有发
 送到它的输出的对象中。然后,在调用serlvet或JSP页面后,过滤器可检查输出,
 如果合适就修改 它,之后发送到客户机。
FilterChain:对此对象调用doFilter以激活与servlet或JSP页面相关的下一个过滤器。
 如果没有另一个相关的过滤器,则对doFilter的调用激活servlet或JSP本身。

创建Filter

  1. 建立一个实现Filter接口的类
    所有过滤器都必须实现javax.servlet.Filter。这个接口包含三个方法,分别为doFilter、init和destroy。

  2. 将过滤行为放入doFilter方法
    doFilter方法为大多数过滤器地关键部分。每当调用一个过滤器时,都要执行doFilter。对于大多数过滤器来说,doFilter执行的步骤是 基于传入的信息的。因此,可能要利用作为doFilter的第一个参数提供的ServletRequest。这个对象常常构造为 HttpServletRequest类型,以提供对该类的更特殊方法的访问。

  3. 调用FilterChain对象的doFilter方法
    Filter接口的doFilter方法以一个FilterChain对象作为它的第三个参数。在调用该对象的doFilter方法时,激活下一个相关的 过滤器。这个过程一般持续到链中最后一个过滤器为止。在最后一个过滤器调用其FilterChain对象的doFilter方法时,激活servlet或页面自身。但是,链中的任意过滤器都可以通过不调用其FilterChain的doFilter方法中断这个过程。在这样的情况下,不再调用JSP页面的serlvet,并且中断此调用过程的过滤器负责将输出提供给客户机。

  4. 对适当的servlet和JSP页面注册过滤器
    web.xml中有两个用于过滤器的元素,分别是:filter和filter-mapping。filter元素向系统注册一个过滤对象,filter-mapping元素指定该过滤对象所应用的URL。

Filter原理
在这里插入图片描述
在这里插入图片描述
Filter应用场景
1、统一POST请求中文字符编码的过滤器
2、控制浏览器缓存页面中的静态资源的过滤器
有些动态页面中引用了一些图片或css文件以修饰页面效果,这些图片和css文件经常是不变化的,所以为减轻服务器的压力,可以使用filter控制浏览器缓存这些文件,以提升服务器的性能。

3、使用Filter实现URL级别的权限认证
在实际开发中我们经常把一些执行敏感操作的servlet映射到一些特殊目录中,并用filter把这些特殊目录保护起来,限制只能拥有相应访问权限的用户才能访问这些目录下的资源。从而在我们系统中实现一种URL级别的权限功能。

4、实现用户自动登陆
首先,在用户登陆成功后,发送一个名称为user的cookie给客户端,cookie的值为用户名和md5加密后的密码。编写一个AutoLoginFilter,这个filter检查用户是否带有名称为user的cookie,如果有,则调用dao查询cookie的用户名和密码是否和数据库匹配,匹配则向session中存入user对象(即用户登陆标记),以实现程序完成自动登陆。

Servlet Filter配置(web.xml详解)

注册Filter

<filter-name> 用于为过滤器指定一个名字,该元素的内容不能为空。
<filter-class> 用于指定过滤器的完整的限定类名。	
<init-param> 用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字, 
<param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
<web-app>
  <filter> 
    <filter-name>testFitler</filter-name>  
    <filter-class>org.test.TestFiter</filter-class>  
    <init-param> 
      <param-name>word_file</param-name>  
      <param-value>/WEB-INF/word.txt</param-value> 
    </init-param> 
  </filter>
</web-app>

映射Filter
元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径

(1)<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中
	声明过的过滤器的名字	
(2)<url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)	
(3)<servlet-name>指定过滤器所拦截的Servlet名称。	
(4)<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,
	可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST
	。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调
	用方式进行拦截。
(5)<filter-mapping> 过滤器拦截配置如果连接目标资源是一个Servlet,
	可以选择url和servlet名称两种配置方式:	
	<!-- 拦截/hello是Servlet 路径 -->
	<url-pattern>/hello</url-pattern>
	<!-- 拦截Servlet 还可以通过Servlet 名称进行拦截 -->
	<servlet-name>HelloServlet</servlet-name>
(6)url-pattern 和 Servlet中路径写法一样,有三种 : 
	完全匹配、目录匹配、扩展名匹配

(7)<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式	
	<dispatcher> 子元素可以设置的值及其意义:	
	REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。
		如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,
		那么该过滤器就不会被调用。	
		INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,
		那么该过滤器将被调用。除此之外,该过滤器不会被调用。		 
	FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,
		那么该过滤器将被调用,除此之外,该过滤器不会被调用。	
	ERROR:如果目标资源是通过声明式异常处理机制调用时,
		那么该过滤器将被调用。除此之外,过滤器不会被调用
<filter>
    <filter-name>loggerfilter</filter-name>
    <filter-class>myfilter.LoggerFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>loggerfilter</filter-name>
    <url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>loggerfilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
</filter-mapping>

Servlet FilterConfig详解(获取web.xml中filter参数)

方法

String getFilterName()  //得到filter的名称。
String getInitParameter(String name)  // 返回在部署描述中指定名称的初始化参		数的值。如果不存在返回null.
Enumeration getInitParameterNames()  //返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext()  //返回Servlet上下文对象的引用。

例子

//web.xml
<web-app> 
  <filter> 
    <!-- 设计过滤处理类,生成静态页面 -->  
    <filter-name>CacheFilter</filter-name>  
    <filter-class>com.jspbook.CacheFilter</filter-class>  
    <!-- 不需要缓存的URL -->  
    <init-param> 
      <param-name>/TimeMonger.jsp</param-name>  
      <param-value>nocache</param-value> 
    </init-param>  
    <init-param> 
      <param-name>/TestCache.jsp</param-name>  
      <param-value>nocache</param-value> 
    </init-param>  
    <!-- 缓存超时时间, 单位为秒 -->  
    <init-param> 
      <param-name>cacheTimeout</param-name>  
      <param-value>600</param-value> 
    </init-param>  
    <!-- 是否根据浏览器不同的地区设置进行缓存(生成的缓存文件为 test.jsp?id=1_zh_CN 的格式) -->  
    <init-param> 
      <param-name>locale-sensitive</param-name>  
      <param-value>true</param-value> 
    </init-param> 
  </filter>  
  <filter-mapping> 
    <filter-name>CacheFilter</filter-name>  
    <url-pattern>*.jsp</url-pattern> 
  </filter-mapping> 
</web-app>
//获取web.xml中filter参数
public void init(FilterConfig filterConfig) {
    String ct = filterConfig.getInitParameter("cacheTimeout");
    if (ct != null) {
        cacheTimeout = 60 * 1000 * Long.parseLong(ct);
    }
    filterConfig.getInitParameter("locale-sensitive"); //得到的就是ture 
    filterConfig.getInitParameter("cacheTimeout");  //得到的就是600 
    filterConfig.getInitParameter(request.getRequestURI());  //得到的就是param - name对应的param - value值ServletContext sc = filterConfig.getServletContext();
}

Filter生命周期

1. 加载和实例化
Web 容器启动时,即会根据 web.xml 中声明的 filter 顺序依次实例化这些 filter。

2. 初始化
Web 容器调用 init(FilterConfig) 来初始化过滤器。容器在调用该方法时,
向过滤器传递 FilterConfig 对象,FilterConfig 的用法和 ServletConfig 类似。
利用 FilterConfig 对象可以得到 ServletContext 对象,
以及在 web.xml 中配置的过滤器的初始化参数。在这个方法中,
可以抛出 ServletException 异常,通知容器该过滤器不能正常工作。
此时的 Web 容器启动失败,整个应用程序不能够被访问。
实例化和初始化的操作只会在容器启动时执行,而且只会执行一次。

3. doFilter	
doFilter 方法类似于 Servlet 接口的 service 方法。当客户端请求目标资源的时候,
容器会筛选出符合 filter-mapping 中的 url-pattern 的 filter,并按照声明
 filter-mapping 的顺序依次调用这些 filter 的 doFilter 方法。在这个链式调用过程
 中,可以调用 chain.doFilter(ServletRequest, ServletResponse) 将请求传给下一
 个过滤器(或目标资源),也可以直接向客户端返回响应信息,
 或者利用 RequestDispatcher 的 forward 和 include 方法,
 以及 HttpServletResponse 的 sendRedirect 
 方法将请求转向到其它资源。需要注意的是,
 这个方法的请求和响应参数的类型是 ServletRequest  和 ServletResponse,
 也就是说,过滤器的使用并不依赖于具体的协议。

4. 销毁
Web 容器调用 destroy 方法指示过滤器的生命周期结束。
在这个方法中,可以释放过滤器使用的资源。
//HttpFilter.java
public class HttpFilter implements Filter {  
  
    @Override  
    public void destroy() {  
        System.out.println("过滤器生命终止");  
    }  
  
    @Override  
    public void doFilter(ServletRequest arg0, ServletResponse arg1,  
            FilterChain arg2) throws IOException, ServletException {  
        HttpServletRequest req = (HttpServletRequest)arg0;  
        // 打印请求的URL  
        System.out.println("filter1 " + req.getRequestURL());  
        arg2.doFilter(arg0, arg1);  
    }  
  
    @Override  
    public void init(FilterConfig arg0) throws ServletException {  
        System.out.println("过滤器生命开始");  
          
        String filterName = arg0.getFilterName();  
        System.out.println("过滤器名为 " + filterName);  
          
        // 遍历过滤器中的参数和对应值  
        Enumeration<String> initParas = arg0.getInitParameterNames();  
        String paraName;  
        String paraValue;  
        while (initParas.hasMoreElements()) {  
            paraName = initParas.nextElement();  
            paraValue = arg0.getInitParameter(paraName);  
            System.out.println(paraName + " = " + paraValue);  
        }  
    }  
}
//web.xml
<filter>  
  <filter-name>httpFilter</filter-name>  
  <filter-class>com.filter.HttpFilter</filter-class>  
    <init-param>  
        <param-name>creater</param-name>  
        <param-value>xxx</param-value>  
    </init-param>  
    <init-param>  
        <param-name>createrDate</param-name>  
        <param-value>2017-05-20</param-value>  
    </init-param>  
</filter>  
<filter-mapping>  
  <filter-name>httpFilter</filter-name>  
  <url-pattern>/*</url-pattern>  
</filter-mapping>
服务器启动时,可以在控制台中看到:	
	过滤器生命开始  
	过滤器名为 httpFilter  
	creater = xxx  
	createrDate = 2017-08-29 

访问http://localhost:8080/servletDemo/ 
	filter1 http://localhost:8080/servletDemo/  

我们停止tomcat服务器,控制台可以看到	
	过滤器生命终止  

实现登陆拦截

在这里插入图片描述
现在要让浏览器无法随意访问WEB-INF的hello.html,必须先登陆
效果
输入:http://localhost:8089/filter/hello
自动跳转到登陆页面
在这里插入图片描述
成功输入用户名和密码后
在这里插入图片描述
web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <filter>
    <filter-name>loggerfilter</filter-name>
    <filter-class>filter.LoginFilter</filter-class>
  </filter>
  <filter-mapping>
	<filter-name>loggerfilter</filter-name>
	<servlet-name>loginServlet</servlet-name>
	<url-pattern>*</url-pattern>
  </filter-mapping>
  
  <servlet>
      <servlet-name>loginServlet</servlet-name>
      <servlet-class>filter.LoginServlet</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>loginServlet</servlet-name>
      <url-pattern>/login</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
      <servlet-name>loginServlet</servlet-name>
      <url-pattern>/check</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
      <servlet-name>loginServlet</servlet-name>
      <url-pattern>/hello</url-pattern>
  </servlet-mapping>

</web-app>

hello.html

<!DOCTYPE html>
<html lang="en">
``
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
    <h3>登陆成功!</h3>
</body>
</html>

login.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
    <h3>登陆</h3>
    <form action="http://localhost:8089/filter/check" method="post">
        用户名: <input type="text" name="username" placeholder="请输入用户名"><br>
        密码  : <input type="password" name="password" placeholder="请输入密码"><br>
        <input type="reset" value="重置">
        <input type="submit" value="登陆">
    </form>
</body>
</html>

Use.java 模拟数据库的用户和秘密

package filter;

import java.util.HashMap;
import java.util.Map;

public class User {
	private static Map<String, String> user = new HashMap<String, String>();
	static {
		user.put("cp", "1234567");
		user.put("china","123456");
	}
	public static Map<String, String> getUser() {
		return user;
	}
}

LoginServlet.java

package filter;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginServlet extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("=============================》进入Servlet");		
		
		String url = request.getRequestURI();
		System.out.println(url);
		if(url.endsWith("/login")) {
//			if(request.getAttribute("flag")!=null && (Boolean) request.getAttribute("flag")) {
//				return;
//			}
//			request.setAttribute("flag", true);
			System.out.println("=============================>我是转发进来的");
			// 这里的转发(/login.html)在web.xml里面没有设置被拦截,如果被(Servlet)"拦截"了的话,那么这里就访问不到"login.html"了
			request.getRequestDispatcher("/login.html").forward(request, response);
		}else if(url.endsWith("/check")){
			System.out.println("=============================>登陆验证");
			String username = request.getParameter("username");
			String password = request.getParameter("password");
			System.out.println("username = " + username);
			System.out.println("password = " + password);
			// 拿到已经注册的用户名和密码
			Map<String, String> users = User.getUser();
			if(users.containsKey(username) && users.get(username).equals(password)) {
				// 在Session里面设置已经登陆过了
				HttpSession session = request.getSession();
				session.setAttribute("username", username);
				session.setAttribute("password", password);
				session.setMaxInactiveInterval(60); // 60秒没请求登陆就失效
				request.getRequestDispatcher("/WEB-INF/hello.html").forward(request, response);
			}else {
				System.out.println("输入的用户名或密码错误");
			}
		}else if(url.endsWith("/hello")){
			System.out.println("============================>登陆成功了");
			request.getRequestDispatcher("/WEB-INF/hello.html").forward(request, response);
		}
		
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(req, resp);
	}
	
}

LoginFilter.java

package filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginFilter implements Filter{

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("进入拦截器");
		// TODO Auto-generated method stub
		// 获得在下面代码中要用的request,response,session对象
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        HttpSession session = servletRequest.getSession();

        // 获得用户请求的URI
        String path = servletRequest.getRequestURI();
        //System.out.println(path);
        
        /*创建类Constants.java,里面写的是无需过滤的页面
        for (int i = 0; i < Constants.NoFilter_Pages.length; i++) {

            if (path.indexOf(Constants.NoFilter_Pages[i]) > -1) {
                chain.doFilter(servletRequest, servletResponse);
                return;
            }
        }*/
        
        // 登陆页面和验证请求(搞了好久,汗....)无需过滤
        if(path.indexOf("/login") > -1 || path.indexOf("/check") > -1) {
        	System.out.println("==============================>无需过滤的拦截");
            chain.doFilter(servletRequest, servletResponse);
            return;
        }
        
        // 从session里取员工工号信息
        String username = (String) session.getAttribute("username");
        String password = (String) session.getAttribute("password");
        // 判断如果没有取到员工信息,就跳转到登陆页面
        if (username == null || "".equals(username) || password == null || "".equals(password)) {
            // 跳转到登陆页面,重定向,前后request不是同一个
           servletResponse.sendRedirect("/filter/login");
            // 转发
        	//servletRequest.getRequestDispatcher("/WEB-INF/login.html").forward(request, response);
        } else {
            // 已经登陆,继续此次请求
            chain.doFilter(request, response);
        }
    }
	
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页