通俗理解Springboot過濾器

通俗理解Springboot過濾器

導航

什麼是過濾器

Spring的過濾器

Filter定義

過濾的物件

典型應用

過濾器的使用

Filter生命週期

過濾器鏈

自定義敏感詞過濾器

新增自定義過濾器

新增 @WebFilter註解

新增 @ServletComponentScan 註解

測試用例

結語

原始碼地址

參考

本節是智客工坊-《Spring Boot 實戰紀實》的第16篇,感謝您的閱讀,預計閱讀時長5min。

通俗地講,過濾器可以簡單理解為“取你所想取”,忽視掉那些你不想要的東西。

通俗理解Springboot過濾器

什麼是過濾器

生活中有很多“過濾器”的例子。

一臺帶有過濾功能的飲水機,裡面就會有個過濾器,可以將汙水中的雜質過濾,從而使進入的汙水變成淨水。

漁民捕魚使用的漁網,也有講究,網眼尺寸要恰到好處,既要捕撈的捕獵到成魚,又要給給小魚苗一個逃生的機會。

建築工地上常見的篩網,通常用於將沙子中細沙過濾。。。

Spring的過濾器也是一樣,我們可以自己在一個沒有過濾器的程式裡面加上過濾器,同時可以選擇攔截下什麼資源,放行什麼資源。

Spring的過濾器

Filter定義

一個駐留在服務端的web元件,可以擷取使用者端和資源之間的請求與響應資訊,並對這些資訊過濾。

通俗理解Springboot過濾器

當Web容器接收到一個對的資源的請求時,它將判斷是否有過濾器(可以有多個過濾器)與這個資源有關聯

如果有,容器把請求交給過濾器處理。在過濾器中,可以改變請求內容,或者重新設定請求的資訊,然後再將請求傳送給目標資源。

當目標資源對請求作出響應後,容器同樣將響應先轉發給過濾器,過濾器可以對響應的內容進行轉換,然後再將響應傳送到客戶端。

在一個Web應用中,可以定義多個過濾器,組成一個過濾器鏈(FilterChain)。過濾器鏈中的每個過濾器負責特定的 操作和任務,客戶端的請求在這些過濾器之間傳遞,直到目標資源。

通俗理解Springboot過濾器

Notes: Filter 不是一個標準的Servlet,不能處理使用者請求,也不能對客戶端生成響應。主要用於對HttpServletRequest進行預處理, 也可以對HttpServletResponse進行處理,是一個典型的處理鏈。

Servlet API中提供了一個Filter介面,開發web應用時,如果編寫的Java類實現了這個介面,則把這個java類稱之為過濾器Filter。

過濾器的英文名稱為 Filter, 是 Servlet 技術中最實用的技術。如同它的名字一樣,過濾器是處於客戶端和伺服器資原始檔之間的一道過濾網,幫助我們過濾掉一些不符合要求的請求,通常用作 Session 校驗,判斷使用者許可權,如果不符合設定條件,則會被攔截到特殊的地址或者基於特殊的響應。

過濾器放在web資源之前,透過Filter技術,可以實現在請求抵達它所應用的web資源(可以是一個Servlet、一個Jsp頁面,甚至是一個HTML頁面)之前截獲進入的請求,並且在它返回到客戶之前截獲輸出請求。

過濾物件

web伺服器管理的所有web資源:例如Jsp, Servlet, 靜態圖片檔案或靜態 html 檔案。

典型應用

對使用者請求進行統一認證

對使用者傳送的資料進行過濾或者替換

對內容進行壓縮,以減小通訊量

過濾器的使用

自定義過濾器需要實現Filter介面,然後重寫它的三個方法

init 方法:在容器中建立當前過濾器的時候自動呼叫

destory 方法:在容器中銷燬當前過濾器的時候自動呼叫

doFilter 方法:主要的業務程式碼編寫方法,可以多次重複呼叫

Notes: doFilter(。。。)實現過濾器的功能。在特定的操作完成之後,可以呼叫chain。doFilter()方法, 將請求傳給下一個過濾器(或目標資源),可以直接向客戶端返回響應資訊,或者利用轉發、重定向將請求轉發到其他資源。

Filter生命週期

當伺服器啟動時,web應用載入後,立即建立這個web應用中的所有的過濾器,過濾器創建出來後立即呼叫init方法執行初始化的操作。

創建出來後一直駐留在記憶體中為後續的攔截進行服務。每次攔截到請求後都會導致doFilter方法執行。

在伺服器關閉或web應用被移除出容器時,隨著web應用的銷燬過濾器物件銷燬。銷燬之前呼叫destory方法執行善後工作。

Filter的建立

Filter的建立和銷燬由WEB伺服器負責。 web 應用程式啟動時,web 伺服器將建立Filter 的例項物件,並呼叫其init方法,完成物件的初始化功能,從而為後續的使用者請求做好攔截的準備工作,filter物件只會建立一次,init方法也只會執行一次。透過init方法的引數,可獲得代表當前filter配置資訊的FilterConfig物件。

Filter的銷燬

Web容器呼叫destroy方法銷燬Filter。destroy方法在Filter的生命週期中僅執行一次。在destroy方法中,可以釋放過濾器使用的資源。

FilterConfig介面

使用者在配置filter時,可以使用為filter配置一些初始化引數,當web容器例項化Filter物件,呼叫其init方法時,會把封裝了filter初始化引數的filterConfig物件傳遞進來。因此開發人員在編寫filter時,透過filterConfig物件的方法,就可獲得:

String getFilterName():得到filter的名稱。

String getInitParameter(String name): 返回在部署描述中指定名稱的初始化引數的值。如果不存在返回null。

Enumeration getInitParameterNames():返回過濾器的所有初始化引數的名字的列舉集合。

public ServletContext getServletContext():返回Servlet上下文物件的引用。

過濾器鏈

A FilterChain is an object provided by the servlet container to the developer giving a view into the invocation chain of a filtered request for a resource。 Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain。——《tomcat-5。5-doc》

Web應用允許多個過濾器來過濾頁面請求——聯想現實生活中的例子是最好理解的啦!比如:為了獲得更加乾淨的水,可能需要多個過濾器來進行過濾。

通俗理解Springboot過濾器

Servlet定義了過濾器介面Filter和過濾器連結口FilterChain,如下:

//// Source code recreated from a 。class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package javax。servlet;import java。io。IOException;public interface Filter { default void init(FilterConfig filterConfig) throws ServletException { } void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException; default void destroy() { }}

//// Source code recreated from a 。class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package javax。servlet;import java。io。IOException;public interface FilterChain { void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;}

所有自定義過濾器都繼承自FIlter介面,並實現其doFilter()方法 FiterChain和Filter的介面的doFilter()方法簽名不同:

//FilterChain中:public void doFilter(Target target) { if (index == filterList。size()) { return; } //獲得下一個過濾器 Filter f = getNextFilter(); //執行一個處理器/過濾器的doFilter()方法,然後在該過濾器的doFilter()方法內回撥此方法 f。doFilter(target, this);}//Filter中:public void doFilter(Target target, FilterChain chain) { //操作 。。。 //回撥FilterChain的doFilter方法 chain。doFilter(target);}

在前面《Spring攔截器》章節中,有講到一個物件被多個攔截器攔截處理時,我們稱這樣的設計模式為責任鏈模式。同樣的道理,一個web請求也可以被多個過濾器來進行處理,這就是FilterChain。

在一個 Web 應用程式中可以註冊多個 Filter 程式,每個 Filter 程式都可以對一個或一組 Servlet 程式進行攔截。如果有多個 Filter 程式都可以對某個 Servlet 程式的訪問過程進行攔截,當針對該 Servlet 的訪問請求到達時,Web 容器將把這多個 Filter 程式組合成一個 Filter 鏈(也叫過濾器鏈)。

Filter 鏈中的各個 Filter 的攔截順序與它們在 web。xml 檔案中的對映順序一致,上一個 Filter。doFilter 方法中呼叫 FilterChain。doFilter 方法將啟用下一個 Filter的doFilter 方法,最後一個 Filter。doFilter 方法中呼叫的 FilterChain。doFilter 方法將啟用目標 Servlet的service 方法。

只要 Filter 鏈中任意一個 Filter 沒有呼叫 FilterChain。doFilter 方法,則目標 Servlet 的 service 方法都不會被執行。

關於責任鏈模式在servlet中程式碼上是如何實現的,有興趣的同學可以進一步閱讀《JAVA與模式》之責任鏈模式。

在Springboot中自定義敏感詞過濾器

場景:

在早期的BBS論壇中有很多發帖和回帖的操作,為了保持論壇的和諧,管理員會人工稽核和刪除一些帶有敏感詞的帖子。

針對這個場景,如果使用Spring框架,我們可以使用filter進行敏感詞的過濾。

新增自定義過濾器

如下自定義過濾器 ReqResFilter 必須實現 javax。servlet。Filter。 然後添加註解 @WebFilter(javax。servlet。annotation。WebFilter),urlPatterns 過濾器要過濾的URL規則配置,filterName 過濾器的名稱。

我們新建一個SensitiveWordFilter並實現javax。servlet。Filter

SensitiveWordFilter。java

package com。zhike。filter;import org。springframework。cglib。proxy。InvocationHandler;import org。springframework。cglib。proxy。Proxy;import org。springframework。util。ResourceUtils;import javax。servlet。*;import javax。servlet。annotation。WebFilter;import java。io。*;import java。lang。reflect。Method;import java。util。ArrayList;@WebFilter(filterName = “SensitiveWordFilter”,urlPatterns = {“/*”})public class SensitiveWordFilter implements Filter { ArrayList list = new ArrayList(); /** * @param * @method 初始化過濾詞的配置 */ @Override public void init(FilterConfig filterConfig) throws ServletException { try { String line; //讀取我們的過濾配置檔案 File resource = ResourceUtils。getFile(“classpath:static/sensitive。txt”); BufferedReader sensitivewords = new BufferedReader(new FileReader(resource)); //把讀取到的資訊,存放到list中 while ((line = sensitivewords。readLine()) != null) { list。add(line); } } catch (FileNotFoundException e) { e。printStackTrace(); } catch (IOException e) { e。printStackTrace(); } } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //建立代理物件,增強getParameter ServletRequest proxy_req = (ServletRequest) Proxy。newProxyInstance(req。getClass()。getClassLoader(), req。getClass()。getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判斷是不是getParameter方法 if (method。getName()。equals(“getParameter”)){ //獲取返回值 String value = (String)method。invoke(req, args); if (value != null){ for (String s : list){ if (value。contains(s)){ value = value。replaceAll(s,“***”); } } } return value; } return method。invoke(req,args); } }); chain。doFilter(proxy_req, resp); } @Override public void destroy() { System。out。println(“——過濾器銷燬——”); }}

@WebFilter註解

@WebFilter 用於將一個類宣告為過濾器,該註解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為過濾器。該註解具有下表給出的一些常用屬性 ( 以下所有屬性均為可選屬性,但是 value、urlPatterns、servletNames 三者必需至少包含一個,且 value 和 urlPatterns 不能共存,如果同時指定,通常忽略 value 的取值 )

@WebFilter 的常用屬性

屬性名

型別(Filter)

描述(Interceptor)

fileterName

String

指定過濾器的Name屬性,等價於

value

String[]

該屬性等價於urlPattems,但兩者不應同時使用

urlPattems

String[]

指定一組過濾器的URL匹配模式。等價於標籤

servlerNames

String[]

指定過濾器將應用於哪些servlet。取值是@WebServlet中的name屬性的取值或者是web。xml中取值。

dispatchTypes

DispatchType

指定過濾器的轉發模式。具體取值包括:FORWARD,INCLUDE,REQUEST,ERROR,ASYNC

initParams

WebInitParam[]

指定一組過濾器初始化引數 等價於標籤

asyncSupported

boolean

宣告過濾器是否支援非同步操作模式,等價於

description

String

該過濾器的描述資訊, 等價於

displayName

String

該過濾器的顯示名,通常配合工具使用 等價於

新增 @ServletComponentScan 註解

在啟動類上加一個註解 @ServletComponentScan 就可以了,然後啟動springboot 訪問你的介面就會看到列印過濾器裡的內容了。

@SpringBootApplication(scanBasePackages=“com。zhike”)@ServletComponentScan(basePackages = “com。zhike”)public class BlogWebappApplication { public static void main(String[] args) { SpringApplication。run(BlogWebappApplication。class, args); }}

測試用例

透過上面的幾個步驟,我們就建立好了一個自定義過濾器。

那如何驗證我們的過濾器起作用呢?

正好我們的首頁有一個輸入框,本來使用者輸入關鍵詞,透過文章標題來篩選文章。 我們以此為例,假設不訊息輸入了“壞蛋”,“笨蛋”這樣的敏感詞(這裡我設定的敏感詞庫為sensitie。txt)。

sensitie。txt中文字如下:

笨蛋壞蛋

期望服務端接收到敏感詞之後,被替換為“***”。

value = value。replaceAll(s,“***”);

我們在DefaultController中Index方法

@RequestMapping(“/index”) public ModelAndView Index( HttpServletRequest request, Article article, @RequestParam(value = “pageNum”,defaultValue = “0”) int pageNum, @RequestParam(value = “pageSize”, defaultValue = “5”) int pageSize) { String title = request。getParameter(“title”); System。out。println(title); 。。。。。}

然後,修改一下/resources/templates/index。html檢視


通俗理解Springboot過濾器

最後,啟動除錯,在搜尋框中輸入“笨蛋”,透過檢視title變數的值,發現他被替換成了“***”。

通俗理解Springboot過濾器

由此可見,我們的過濾敏感詞功能實現了。

結語

本章節介紹了Spring中過濾器的原理和機制,以及在springboot中如何使用。

原始碼地址

git地址: https://github。com/zhikecore/superblog

參考

java 過濾器 Filter

《Java與模式》

https://www。cnblogs。com/he-px/p/7145626。html

https://www。cnblogs。com/zhangyaxiao/p/8296162。html

https://blog。csdn。net/u012410733/article/details/51746178

責任鏈模式的典型應用——Tomcat Filter中的責任鏈模式

Filter、FilterChain、FilterConfig 介紹

《JAVA與模式》之責任鏈模式