1. 程式人生 > >Java Web基礎知識之Filter:過濾一切你不想看到的事情

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,則也不會經過這個過濾器。

相關文章:

相關推薦

Java Web基礎知識Filter過濾一切看到的事情

不要相信客戶端, 所以做後端的人都應該銘記的事情。因為前端傳過來的資料並不總是合法和有效的,所以後端是對訪問資源的最後一道保護傘。之前我們在Spring中說到過AOP程式設計,AOP基礎知識,它就可以在執行我們的方法之前進行一些預處理和驗證來保護後端的資源。不難想到她的實現

Java Web基礎知識安全人生苦短,注意安全

關於web程式中的安全方面,想必大多數人都不甚瞭解,或者說感覺沒有必要了解,身邊開發網站的人主要就是注重後臺的功能和前臺的介面,不要說程式的安全問題,甚至後臺資料庫訪問的問題可能都沒有下大力氣解決。但是這又是和我們密切相關的一個問題,每天看到網站哪個系統或者網站又出現安全問

Java Web基礎知識Servlet(3)Session管理

Session 管理是Web應用開發中的一個重要的內容,其實每天我們瀏覽網站,網站的後臺都是通過這門技術來記錄我們的瀏覽狀態,最典型的就是登入,每次你在網站上登入一次,當跳轉到該網站的任何其他頁面都不會再次要求你登入,這就是使用了Session管理技術。那麼問題來了我們為什

Java Web基礎知識檔案下載下載檔案的時候到底發生了什麼?

從網上下載檔案幾乎是每個人都會遇到的,不管是圖片、文字檔案還是一些視訊,但是我們真的知道在下載的過程中發生了什麼嗎?本文章就學習一下其中的原理。 關於檔案下載存在靜態下載和動態下載兩種,靜態下載是比較容易的,我們平常在網上對很多圖片和和視訊等的下載有很多其實就是靜態下載,那

Java Web基礎知識檔案上傳檔案上傳一窺究竟

其實檔案上傳的文章已經寫得很多了,但是好多文章都是都是說明了怎麼實現,沒有說這個過程到底發生了什麼(會不會引來仇恨。。),其實實現檔案上傳並不複雜,也沒有多少程式碼,但是要是清楚的明白其中的原理還是費點功夫的,這裡就還原檔案上傳的整個過程。 其實關於檔案上傳在最早之前是使用

Java Web基礎知識Servlet容器初始化(無web.xml)

在之前典型的Java Web程式中,部署描述符web.xml是必不可少的,在這裡我們需要配置各種元件,包括Servlet、Filter和Listener等,如果使用過SpringMVC的話,應該會對在web.xml中配置org.springframework.w

scrapy基礎知識 RedisCrawlSpider

span 準備 動態 none efi pytho sel ext import 這個RedisCrawlSpider類爬蟲繼承了RedisCrawlSpider,能夠支持分布式的抓取。因為采用的是crawlSpider,所以需要遵守Rule規則,以及callback不能寫

Java web基礎學習開發環境篇

work eclipse 軟件 ase java lips pda down ava Tomcat + Eclipse添加Java EE插件 因為之前進行Java SE學習已經配置了JDK,安裝了Eclipse for Java SE,所以選擇了在Eclipse上添加插件的

Java Web 學習筆記嵌入式web伺服器Jetty的基本使用

Jetty 是一個開源的servlet容器,具有易用性,可擴充套件性,易嵌入性等特點。通過少量的程式碼,開發者就可以在程式中以嵌入的方式執行一個web伺服器。 下面介紹一些Jetty使用的方式:

Java Web 學習筆記Java HttpURLConnection保持會話的方法

在Java Web開發中,會話保持是伺服器識別客戶端(一般指瀏覽器)的方式。對此,各大瀏覽器都是支援會話保持的。然而在開發者通過Java API HttpURLConnection 開發網路請求工具的

java Web 基礎知識學習心得

javaWeb第二週 session: 維護伺服器和客戶端聯絡的一種技術 當瀏覽器訪問伺服器時,伺服器會建立一個Session物件,(HttpSession) Map結構儲存; 該物件有一個唯一的標識

Java Web 學習筆記十四RestEasy新增Filter過濾器預處理請求

RestEasy新增Filter過濾器預處理請求 前提 定義filter過濾器,預處理http請求 在resteasy框架下配置filter 實現功能 攔截http請求,獲取請求頭中的

python基礎知識將item寫入JSON文件

ext self 它的 基礎 string 寫入 lan raw pre pipelines.py import json class xxPipeline(object): def __init__(self): self.filename=ope

scrapy基礎知識 使用FormRequest.from_response()方法模擬用戶登錄

imp gin req params level spa 密碼 重寫 start 通常網站通過 實現對某些表單字段(如數據或是登錄界面中的認證令牌等)的預填充 使用Scrapy抓取網頁時,如果想要預填充或重寫像用戶名、用戶密碼這些表單字段, 可以使用 FormRequest

scrapy基礎知識 pycharm 調試小技巧

.py 小技巧 char line awl spi cmd 調試 基礎知識 在項目根目錄下新建main.py文件,用於調試 from scrapy.cmdline import executeexecute(["scrapy","crawl","MySpider"])scr

scrapy基礎知識 Scrapy-Redis分布式策略

空間 spider head spi 指紋 負責 edi all redis Scrapy-Redis分布式策略: 假設有四臺電腦:Windows 10、Mac OS X、Ubuntu 16.04、CentOS 7.2,任意一臺電腦都可以作為 Master端 或 Slave

scrapy基礎知識 關於爬蟲部分一些建議

限制 支持 結束 攜程 target 經理 框架 實際應用 分享 1.盡量減少請求次數,能抓列表頁就不抓詳情頁,減輕服務器壓力,程序員都是混口飯吃不容易。 2.不要只看 Web 網站,還有手機 App 和 H5,這樣的反爬蟲措施一般比較少。 3.實際應用時候,一般防守方做到

java基礎知識圖形程序設計-1

public clas location image ima 線程 圖形用戶界面 位置 title 經過一段時期java語法的學習,從現在開始,我將進行圖形用戶界面(GUI)的java程序學習。以下是我對此的一點學習筆記。 在java1.0出現初期,采用了A

Python基礎知識hello world,註釋,變量,數據類型

我們 重要 一個 yield code oba () turn 編程語言   從接觸編程語言以來,在我腦海裏經常有三個問號: 這是什麽? 這個有什麽用? 這個怎麽用?   我覺得初學一個東西,把這三個問號都搞明白,那麽剩下的就是孰能生巧的過程了,在接下來的博客中,每個知

Java基礎知識初始化——初始化順序

子類 初始 zab 不清楚 static urn extend rgs end 概述 在Java中所有的變量在使用前都需要初始化。如果我們不初始化,則編譯器會為變量初始化一個默認值(例如,int類型的變量的初始化默認值是0)。但是初始化的順序是什麽樣的,很多人搞不清楚(我