1. 程式人生 > 實用技巧 >過濾器Filter

過濾器Filter

Filter過濾器

1.過濾器是什麼?

過濾器: 可以把"不和諧"的東西給過濾掉/篩選掉/排除掉.

生活中的過濾器:帶有過濾功能的淨水器,濾紙,香菸的過濾嘴,測試,丈母孃.
程式中的過濾器:雙向過濾器

在Java中最小的程式單元是類,程式中的過濾器就是一個特殊的類.
Servlet/Filter是Web的一個元件.

2.過濾器的作用

我們可以簡單理解為:過濾器處於客戶端和服務端之間

過濾器可以對所有的請求或響應做攔截操作

  1. 以常規的方式呼叫資源(Servlet/JSP);

  2. 利用修改過的請求資訊呼叫資源;

    (可以將請求過來的資料修改後 再取呼叫資源)

  3. 呼叫資源之後,但在響應到客戶端之前,對響應做出修改;

    (可以將返回的響應修改後,交給客戶端)

  4. 阻止當前資源呼叫,代之轉到其他資源.

3.過濾器的應用

首先說明一下開發中常用的思想

1. DRY原則

Don't Repeat Yourself 開發中拒絕程式碼重複(重複會帶來巨大的維護成本)

2. 責任分離原則

各自做各自最擅長的事情

過濾器在開發中的運用:

  • 可以對請求中的字元做編碼.

    有時候我們會給Servlet設定編碼格式

    例如:request.setCharacterEncoding("UTF-8"); 但是要是很多Servlet都設定 造成了程式碼重複 所以我們可以在Filter中設定編碼格式

  • 登陸驗證過濾器.

    登陸驗證操作 同樣也是為了避免程式碼重複

    將登陸驗證操作寫在Filter中 後面請求Servlet資源就不需要驗證了

  • 敏感字(非法文字)過濾.

    有時候請求來的資料中會帶有一些敏感詞彙

    經過Filter時 ,會將這些敏感詞彙代替或者轉義

  • 做MVC框架中的前端控制器.(處理所有請求共同的操作,再分發)

4.過濾器的開發和使用

因為Servlet和Filter都是Web的元件,這裡可以使用 我們可以進行對比記憶。

Servlet開發

1:自定義一個類(XxxServlet),實現於javax.servlet.Servlet介面.
2:實現Servlet介面中的方法(init(初始化方法),service(處理請求)).
3:告訴Tomcat來幫我們管理該Servlet程式(1:使用web.xml 做配置,2:WebServlet("/資源名")).

<servlet>
       <servlet-name>Servlet的別名</servlet-name>
       <servlet-class>自定義Servlet的全限定名</servlet-class>
  </servlet>
  <servlet-mapping>
       <servlet-name>Servlet的別名</servlet-name>
       <url-pattern>/資源名稱</utl-pattern>
  </servlet-mapping>

注意:此時的url-pattern的文字內容是外界訪問是Servlet的資源名稱.

Filter開發

1:自定義一個類(XxxFilter),實現於javax.servlet.Filter介面.
2:實現Filter介面中的方法(init(初始化方法),doFilter(執行過濾操作)).

在啟動Tomcat的時候,就建立好物件,並呼叫init方法做初始化操作.

3:告訴Tomcat來幫我們管理該Filter程式(1:使用web.xml做配置,2:WebFilter("/資源名")).

  <filter>
       <filter-name>Filter的別名</filter-name>
       <filter-class>自定義Filter的全限定名</filter-class>
  </filter>
  <filtert-mapping>
       <filter-name>Filter的別名</filter-name>
       <url-pattern>/資源名稱</utl-pattern>
  </filter-mapping>

注意:此時的url-pattern的文字內容是Filter對哪一些資源做過濾操作.
如: /hello.jsp :說明當前Filter只會對/hello.jsp做攔截/過濾.
/employee :說明當前Filter只會對/employee資源(Servlet)做過濾.
/system/* :說明當前Filter只會對以/system/作為字首的資源路徑做攔截.

FilterChain(過濾器鏈)

​ FilterChain(過濾器鏈):多個過濾器按照一定的順序,排列起來.抽象的可以看為 多個過濾器組成一條鏈

程式中,存在多個過濾器的時候,過濾器的先後執行順序由誰來決定

由在web.xml中:配置的的先後順序來決定.

過濾器的對映細節

1:Filter中的url-pattern的文字內容是Filter對哪一些資源做過濾操作.
如: /hello.jsp :說明當前Filter只會對/hello.jsp做攔截/過濾.
/employee :說明當前Filter只會對/employee資源做過濾.
/system/* :說明當前Filter只會對以/system/作為字首的資源路徑做攔截.
/* :說明對所有的資源做過濾.
注:url-pattern 可以有多個 可對很多資源同時做攔截操作

2:Filter的dispatcher(表示對哪些動作做過濾).

攔截器預設為只對請求做攔截

5.請求編碼過濾器

CharacterEncodingFilter:

請求編碼過濾器就是在過濾器中設定請求的資料的編碼格式

這樣就不需要在後面的Servlet中設定這一行程式碼了

req.setCharacterEncoding("UTF-8");

但是我們一般在原始碼中不能明文寫自己設定的編碼 存在硬編碼問題

應該將編碼格式設定在初始化引數中,從初始化引數獲取

可以在web.xml中配置filter時 設定初始化引數 這樣利於維護 只需要該配置檔案即可

<!-- 配置初始化引數 自定義編碼 -->
  	<init-param>
  		<param-name>encoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>

將encoding設定為成員變數 之後可以在請求編碼過濾器Filter的init方法中取出這個encoding

private String encoding;
	@Override
	public void init(FilterConfig config) throws ServletException {
		// 編碼要從配置web.xml中獲取  不能用明文顯示
		this.encoding = config.getInitParameter("encoding");
	}

得到設定的編碼格式之後,我們在doFilter中開始設定

@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 型別轉換  轉為http型別
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;
        // 應用中沒有設定編碼(req.getCharacterEncoding() == null 的情況)
        // 並且我自己設定了編碼 則設定。  若為false,就使用預設編碼(ISO8859-1)
		if (hasLength(encoding) && req.getCharacterEncoding() == null) {
			req.setCharacterEncoding(encoding);
		}
		
		//放行
		chain.doFilter(req, resp);
	}

上述問題是第一種情況:應用中沒有設定編碼並且我自己設定了編碼 則設定

但是我們還要考慮一種情況,就是之前已經有人設定了編碼,我又定義了編碼,此時是否使用我定義的編碼

這裡要引出一個 是否強制編碼的問題 (是否使用我的編碼)

是否強制編碼問題

這裡我們還是可以在web.xml中filter中設定一個引數 表示是否強制編碼 預設為false

<!-- 設定是否強制編碼  -->
  	<init-param>
  		<param-name>force</param-name>
  		<param-value>false</param-value>
  	</init-param>

跟上述方法一樣 將其設定為成員變數 從init方法中獲取

// 是否強制編碼
	private Boolean forceEncoding  = false;

	@Override
	public void init(FilterConfig config) throws ServletException {
		// 編碼要從配置web.xml中獲取  不能用明文顯示
		this.encoding = config.getInitParameter("encoding");
		forceEncoding = Boolean.valueOf(config.getInitParameter("force"));
	}

考慮這種情況時,設定編碼的判斷就要再加一項 判斷是否要硬編碼 這樣就解決了編碼問題

@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 型別轉換  轉為http型別
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;
		
		// 設定編碼
		// 1.應用中沒有設定編碼(req.getCharacterEncoding() == null 的情況)並且我自己設定了編碼
		// 2.應用中已經存在編碼但是要使用自定義的編碼  : 強制使用
		if (hasLength(encoding) && req.getCharacterEncoding() == null || forceEncoding) {
			req.setCharacterEncoding(encoding);
		}
		
		//放行
		chain.doFilter(req, resp);
	}

6.登入驗證過濾器

CheckLoginFilter

首先我們要明確 登入驗證過濾器 是要過濾那些需要驗證身份的頁面

而登入頁面和接收登入請求的Servlet是不需要被過濾的

所以,如果我們將CheckLoginFilter的url-pattern設定為 /* 則要將這兩項去除過濾

<filter>
 	<filter-name>CheckLoginFilter</filter-name>
 	<filter-class>com.yhnit._03_checklogin.CheckLoginFilter</filter-class>
 </filter>
 <filter-mapping>
 	<filter-name>CheckLoginFilter</filter-name>
 	<url-pattern>/*</url-pattern>
 </filter-mapping>

我們可以將不需要過濾的資源定義在一個數組中,但是這裡還是不嚴謹,不應該明文顯示,應該從初始化引數中取出 但是這個過濾器用的不多,更多的是攔截器,所以我這裡從簡了

// 定義不需要被過濾的資源
// 其實這裡不應該使用明文設定  應該從配置檔案web.xml中獲取初始化引數  後面要學攔截器
	private String[]  UncheckUris = {"/login.jsp","/login"};

doFilter方法 判斷當前過濾資源是否是 不需要過濾的資源,如果是,則放行。 不是,則要身份判斷。

@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;
		
		// 表示當前過濾的資源
		String uri = req.getRequestURI();
		System.out.println("當前被過濾的資源" + uri);
		// 登陸頁面和登陸Servlet不需要過濾
		if (!Arrays.asList(UncheckUris).contains(uri)) {
			Object user = req.getSession().getAttribute("USER_IN_SESSION");
			if (user == null) {
				resp.sendRedirect("/login.jsp");
				return;
			}
		}
        // 已經登入 放行
		chain.doFilter(req, resp);
	}

7.敏感字過濾器

敏感字過濾器 主要是客戶端(瀏覽器)填寫表單資料等資訊時,存在敏感字,提交資料時,過濾器可以先將敏感字進行處理,比如轉義或者變為*符號,然後Servlet獲取的引數就是過濾器處理後的資料值

設計思想:

這裡最主要的是 重寫getParameter 這個方法 讓Servlet處理請求時,用這個重寫的方法,
因為重寫的方法中有過濾功能

定義一個包裝類MessageRequestWapper 裡面重寫getParameter 這個方法 方法裡有過濾功能

在doFilter方法中將 請求req 包裝為請求Servlet的請求

HttpServletRequest requestWapper = new MessageRequestWapper(req); 

當請求Servlet時,Servlet中的req引數其實就是現在的包裝後的請求requestWapper

然後呼叫包裝類重寫的getParameter 方法 即可完成過濾功能。
過濾功能實現(將敏感字寫在stopwords集合中):