Java Web基础知识之Filter:过滤一切你不想看到的事情

不要相信客户端, 所以做后端的人都应该铭记的事情。因为前端传过来的数据并不总是合法和有效的,所以后端是对访问资源的最后一道保护伞。之前我们在Spring中说到过AOP编程,AOP基础知识,它就可以在执行我们的方法之前进行一些预处理和验证来保护后端的资源。不难想到她的实现方式和本篇要说的过滤器的实现原理应该是相同的,都是通过Java的动态代理实现的(自己的理解)。

在Java Web的开发中,过滤器用于拦截请求,并对ServletRequest对象进行处理,我们可以想到的,它可以用来验证权限、加密和解密、Session检查、数据转换、防盗链等等。可以看出,它在web应用开发中是十分重要的。

一、 Filter配置使用

1、 配置方式

Filter的使用有两种配置方式,和Servlet的使用类似,分别是使用注解和部署描述文件web.xml。

使用注解配置Filter,如下:

@WebFilter(filterName="myfilter", urlPatterns="/myHttpServlet",
	initParams={
			@WebInitParam(name="server", value="www.baidu.com"),
			@WebInitParam(name="port", value="443")
		}
	)
public class MyServletFilter implements Filter {}

这里和Servlet类似,分别配置了过滤器的名称、应用的url,这里url是指如果客户端访问Servlet时,如果servlet的url和这里的url相匹配,则会先执行过滤器类的方法。

另外还可以配置初始化参数。

在web.xml文件中配置如下:

<filter>
	<filter-name>myServletFilter1</filter-name>
	<filter-class>com.javaservlet.servlet.filter.MyServletFilter1</filter-class>
	<init-param>
		<param-name>server</param-name>
		<param-value>www.baidu.com</param-value>
	</init-param>
	<init-param>
		<param-name>port</param-name>
		<param-value>443</param-value>
	</init-param>
</filter>

<filter-mapping>
	<filter-name>myServletFilter1</filter-name>
	<url-pattern>/myHttpServlet</url-pattern>
</filter-mapping>

注意在文件中配置Filter的时候要将该过滤器的配置放到Servlet配置之前。

在配置的属性中还有其他的,可以参考javaDoc看一下。

2、 基本API

Filter涉及的类有如下几个:

  • javax.servlet.Filter:包含有Filter的生命周期方法,在servlet容器初始化时或者销毁时被调用;
  • javax.servlet.FilterConfig:包含有关于Filter的配置信息,里边最重要就是它的初始化参数;
  • javax.servlet.FilterChain:是servlet容器提供的来实现多个过滤器之间的调用;

Filte类和Servlet差不多,只是由三个生命周期方法组成,那么调用时机应该也是相同的,容器启动时调用init()方法,容器销毁时调用destroy()方法。当调用与过滤器相关的资源时则会调用doFilter()方法。如下容器启动时:

本程序是接着上一篇Java Web基础知识之Listener:监控Servlet的每个动作而来,所以程序是相同的,可以看到容器启动时首先Servlet容器初始化被监听器发现调用监听器的方法,之后就是初始化过滤器,初始化过滤器之后才会初始化Servlet,最后才是添加一个属性
这才是Servlet容器的一个完整的启动过程。

当容器销毁时,发生的事情如下:

容器销毁时正好的启动时是相反的,首先销毁Servlet实例,然后是销毁Filter,最后是监听器监听到容器销毁并调用相关方法。

在FilterConfig中最重要的就是获取初始化参数,这和Servlet是相似的,如下:

filterConfig.getInitParameter("server");

Enumeration<String> enums = filterConfig.getInitParameterNames();
while(enums.hasMoreElements()){
	String paramName = enums.nextElement();
    String paramValue = filterConfig.getInitParameter(paramName);
    System.out.println(paramName + "-" + paramValue);
}

另外FilterConfig中还有两个方法,其中getServletContext()是比较重要的,它可以获取ServletContext中的很多信息:

filterConfig.getFilterName();
ServletContext context = filterConfig.getServletContext();

最后是FilterChain类,个人感觉这是Filter里面最重要的类,只有它是和用户要访问的资源是直接相关的。

doFilter()方法才是我们实现具体的拦截器逻辑的地方,它可以修改请求的内容和属性,或者在响应中添加一个Http标头,完成自己的逻辑之后要调用FilterChain的doFilter()方法,将Servlet容器提供的请求实例和响应实例转发出去,为什么需要这个方法呢?简单说一个资源对应的过滤器可能不止一个,一个过滤器也会拦截多个请求,基于这种机制,所以一个资源实际上是对应一个过滤器链,每当一个过滤器处理结束后,就将请求转发给其他的过滤器,直到最后一个过滤器处理完成后,就会将请求和响应发给对应的Servlet。

注意:和Servlet类似,默认情况下servlet容器也是为每个过滤器类创建一个实例,也即是单实例,那么不可避免的就会遇到多线程的问题,所以这点要注意。

下面是完整的Filter实例:

@WebFilter(filterName="myfilter", urlPatterns="/myHttpServlet",
	initParams={
			@WebInitParam(name="server", value="www.baidu.com"),
			@WebInitParam(name="port", value="443")
		}
	)
public class MyServletFilter implements Filter {

	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("myServletFiler init");

		filterConfig.getInitParameter("server");

		Enumeration<String> enums = filterConfig.getInitParameterNames();
		while(enums.hasMoreElements()){
			String paramName = enums.nextElement();
		    String paramValue = filterConfig.getInitParameter(paramName);
		    System.out.println(paramName + "-" + paramValue);
		}

		filterConfig.getFilterName();
		ServletContext context = filterConfig.getServletContext();
	}

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

	public void destroy() {
		System.out.println("myServletFiler destroy");
	}

}

二、 Filter作用顺序

前面我们主要是集中在一个过滤器上,但是实际情况下,可能(因该是大多是)不止一个过滤器,所以过滤器之间顺序就很重要,这样才不会使业务逻辑混乱。这里我们做一个测试,主要有4个过滤器,两个配置使用@WebFilter配置,两个使用web.xml配置。在每个过滤器都通过打印来实现标识它们的初始化、销毁和进行过滤,同时它们都对同一资源进行过滤,如下:

public class MyServletFilter1 implements Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("MyServletFilter1 init");
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("MyServletFilter1 do filter");
		chain.doFilter(request, response);
	}

	public void destroy() {
		System.out.println("MyServletFilter1 destroy");
	}
}
public class MyServletFilter2 implements Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("MyServletFilter2 init");
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("MyServletFilter2 do filter");
		chain.doFilter(request, response);
	}

	public void destroy() {
		System.out.println("MyServletFilter2 destroy");
	}
}
@WebFilter(filterName="myServletFilter3", urlPatterns="/myHttpServlet")
public class MyServletFilter3 implements Filter {

	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("MyServletFilter3 init");
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("MyServletFilter3 do filter");
		chain.doFilter(request, response);
	}

	public void destroy() {
		System.out.println("MyServletFilter3 destroy");
	}
}
@WebFilter(filterName="myServletFilter4", urlPatterns="/myHttpServlet")
public class MyServletFilter4 implements Filter {
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("MyServletFilter4 init");
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("MyServletFilter4 do filter");
		chain.doFilter(request, response);
	}

	public void destroy() {
		System.out.println("MyServletFilter4 destroy");
	}
}

MyServletFilter1和2都是在web.xml中进行配置的,3和4都是使用注解配置的。

<filter>
	<filter-name>myServletFilter1</filter-name>
	<filter-class>com.javaservlet.servlet.filter.MyServletFilter1</filter-class>
</filter>

<filter>
	<filter-name>myServletFilter2</filter-name>
	<filter-class>com.javaservlet.servlet.filter.MyServletFilter2</filter-class>
</filter>

<filter-mapping>
	<filter-name>myServletFilter2</filter-name>
	<url-pattern>/myHttpServlet</url-pattern>
</filter-mapping>

<filter-mapping>
	<filter-name>myServletFilter1</filter-name>
	<url-pattern>/myHttpServlet</url-pattern>
</filter-mapping>

Filter的启动顺序:

在测试过程中的filter的启动顺序一直是2143,好像没有什么规律,但是可以发现,如果一个web应用程序中的过滤器是固定不变的,哪个这些过滤器的启动顺序就是不变的,除非有新的过滤器加入进来。

Filter的销毁顺序:

在测试过程中的filter的销毁顺序也一直是2143,如果一个web应用程序中的过滤器是固定不变的,哪个这些过滤器的启动顺序就是不变的,除非有新的过滤器加入进来;而且发现filter的销毁顺序和它们的启动顺序是一致的。

Filter的作用顺序:

在web.xml中指定过滤器的调用顺序,是由filterMapping元素决定的,哪个filter的filterMapping元素在前面则哪个filter首先被调用,而不是由filter元素决定的。

三、 Filter的dispatcher

Filter是有作用范围的,我们平常都是使用Filter作用于Request,这也是Filter中dispatcherTypes属性的默认值,这个属性的意思是由该过滤器管理的资源是通过什么样的方式访问到的,可以是请求、转发、声明式错误、包含等,但是我们还可以配置使Filter作用于其他范围,这是由dispatcherTypes属性决定的,它有如下几个值:

  • DispatcherType.REQUEST
  • DispatcherType.FORWARD
  • DispatcherType.INCLUDE
  • DispatcherType.ERROR
  • DispatcherType.ASYNC

Filter属性的默认值就是DispatcherType.REQUEST,由于它是一个数组属性,所以可以配置多个值。配置方法可以使用注解,也可以使用web.xml进行配置。

使用注解:

@WebFilter(filterName="myfilter", urlPatterns="/myHttpServlet",
	initParams={
			@WebInitParam(name="server", value="www.baidu.com"),
			@WebInitParam(name="port", value="443")
		},
	dispatcherTypes={DispatcherType.REQUEST, DispatcherType.FORWARD}
	)
public class MyServletFilter implements Filter {}

使用web.xml,如下:

<filter-mapping>
	<filter-name>myServletFilter2</filter-name>
	<url-pattern>/myHttpServlet</url-pattern>
	<dispatcher>ERROR</dispatcher>
	<dispatcher>FORWARD</dispatcher>
	<dispatcher>REQUEST</dispatcher>
</filter-mapping>

注意在web.xml中配置的时候,使用的部署描述符(web.xml)的版本要在2.3以上才会有这个属性。

配置该过滤器管理的资源当被直接调用或者通过转发调用时起作用。我们可以在servlet中进行调用,这里有两个servlet供使用:

下面的Servlet是被过滤器管理的:

@WebServlet(name="myHttpServlet", urlPatterns="/myHttpServlet")
public class MyHttpServlet extends HttpServlet {......}

下面的Servlet是用来调用上面的Servlet的:

@WebServlet(name="myHttpServlet1", urlPatterns="/myHttpServlet1", loadOnStartup=1)
public class MyHttpServlet1 extends HttpServlet {
	private static final long serialVersionUID = 6446908355902952649L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("MyHttpServlet1 request work");
		System.out.println("current thread :" + Thread.currentThread().getName());

		resp.getWriter().write("myHttpServlet1");

		req.getRequestDispatcher("/myHttpServlet").forward(req, resp);
//		req.getRequestDispatcher("/myHttpServlet").include(req, resp);
	}

分析如下:

  • 当在上述的Filter中没有DispatcherType.FORWARD时,那么上边通过getRequestDispatcher()调用forward()来访问MyHttpServlet时不会调用过滤器方法,使用forward()方法只会返回最后一个被调用的资源;
  • 当在上述的Filter中没有DispatcherType.INCLUDE时,那么上边通过getRequestDispatcher()调用include()来访问MyHttpServlet时不会调用过滤器方法,使用include()方法则会将所有经过的资源全部返回;

还有一个是对错误的声明式处理,是在web.xml中进行配置的,如下:

<error-page>
	<error-code>404</error-code>
	<!-- error-code or exception-type can be chosen only one -->
	<!-- <exception-type>java.lang.NullPointerException</exception-type> -->
	<location>/myHttpServlet</location>
</error-page>

当在发生404错误时,则会访问到/myHttpServlet对应的Servlet,这时候如果Filter中没有配置DispatcherType.ERROR,则也不会经过这个过滤器。

相关文章:

时间: 2024-10-12 17:00:08

Java Web基础知识之Filter:过滤一切你不想看到的事情的相关文章

java web基础 --- URL重定向Filter

java web基础 --- URL重定向Filter httpRequest.getRequestDispatcher("/helloWorld").forward(httpRequest, response); 服务器端转发,不改变浏览器端URL地址,常用 httpResponse.sendRedirect(uri); 浏览器端重定向,改变URL在浏览器地址栏 URLOverWriteFilter.java 1 package com.web1.util; 2 3 import j

Java Web基础知识之Listener:监控Servlet的每个动作

之前学习了很多涉及servlet的内容,本小结我们说一下监听器,说起监听器,编过桌面程序和手机App的都不陌生,常见的套路都是拖一个控件,然后给它绑定一个监听器,即可以对该对象的事件进行监听以便发生响应,从本质上来说这些都是观察者模式的具体实现,在web程序中的监听器也不例外. 在Java Web程序中使用监听器可以通过以下两种方法: 通过注解@WebListener来标识一个自定义的监听器: @WebListener public class CustomListener implements

开篇--Java Web基础知识

本篇主要是博客的开篇,是一些很基础的知识,我写博客的目的很简单.主要是供自己学习使用,督促自己学习,希望自己能坚持下来. 当前三大主流的Web动态网页技术是PHP(Personal Home Page Tools).ASP(Active Server Pages)和JSP(Java Server Pages). JSP工作原理 3.JSP的体系结构 ①Model 1 ②Model 2(即MVC) 4.Struts2工作原理 主要过程:客户请求服务器-->请求被提交到一系列的过滤器或拦截器(最后到

Java Web基础知识之安全:人生苦短,注意安全

关于web程序中的安全方面,想必大多数人都不甚了解,或者说感觉没有必要了解,身边开发网站的人主要就是注重后台的功能和前台的界面,不要说程序的安全问题,甚至后台数据库访问的问题可能都没有下大力气解决.但是这又是和我们密切相关的一个问题,每天看到网站哪个系统或者网站又出现安全问题都感觉离自己很遥远,其实这只是一个错觉,还是那句话--人生苦短,注意安全(某些人不要理解错了,说的就是你..).写这篇文章的时候,恰好想起来本屌丝考大学报志愿的时候,那时候北邮新开了一门专业叫信息安全,那个年代还不是很火,但

Java Web基础知识 2

1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <%@ page import="java.util.*"%> 4 <%@ page import="java.sql.*" %> 5 <%@ page errorPage=&quo

Java Web基础知识 1

1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <%@ page import="java.util.*"%> 4 <%@ page import="java.sql.*" %> 5 <%@ page errorPage=&quo

Java Web 基础 --- Filter 综述

摘要: 伴随J2EE一起发布的Servlet规范中还包括一个重要的组件--过滤器(Filter).过滤器可以认为是Servlet的一种加强版,它主要用于对用户请求进行预处理以及对服务器响应进行后处理,是个典型的处理链.Servlet规范使用了三个接口对过滤器进行了抽象,即Filter是对具体过滤器的抽象,FilterChain是基于AOP理念对责任链方面的抽象,FilterConfig则是对Filter配置的抽象.本文概述了Filter的提出动机.工作原理.使用流程和应用实例,并指出Java W

Java web基础总结七之—— Filter与Listener

Java web基础总结七之-- Filter与Listener 一.Filter 1. Filter简介 Filter是Servlet体系中非常重要的一个技术.Filter的意思是过滤器,那么它过滤的是什么呢?就是web服务器管理的所有web资源.例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能.它的代码逻辑执行在访问这些资源之前,从而进行一些特殊的操作.例如实现权限访问控制.编码和词汇的过滤等一些高级功能. Servlet API中提供了一

从Java Web 基础看SSH架构

Java Web开发现在已然成为大型Wed项目的标准,之前一直盲目的使用框架,往往知其然不知其所以然.在经过一段时间的学习与开发,大概掌握了其脉络的基础上,对其做一定总结. 一.Java Web 基础 一个典型的Java Web项目往往包含这些元素:Jsp页面.Servlet.Listener.Filter,以及配置文件web.xml.其中: Jsp和Servlet基本是一回事,主要用来响应客户端的请求.当然Jsp中可以直接嵌入HTML标签,主要还是负责展现. Listener则是负责监听Web