一文詳解JavaWeb過濾器(Filter)
基本介紹
過濾器,顧名思義就是對事物進(jìn)行過濾的,在Web中的過濾器,當(dāng)然就是對請求進(jìn)行過濾,我們使用過濾器,就可以對請求進(jìn)行攔截,然后做相應(yīng)的處理,實(shí)現(xiàn)許多特殊功能。如登錄控制,權(quán)限管理,過濾敏感詞匯等.
過濾器原理
當(dāng)我們使用過濾器時,過濾器會對游覽器的請求進(jìn)行過濾,過濾器可以動態(tài)的分為3個部分,1.放行之前的代碼,2.放行,3.放行后的代碼,這3個部分分別會發(fā)揮不同作用。
- 第一部分代碼會對游覽器請求進(jìn)行第一次過濾,然后繼續(xù)執(zhí)行
- 第二部分代碼就是將游覽器請求放行,如果還有過濾器,那么就繼續(xù)交給下一個過濾器
- 第三部分代碼就是對返回的Web資源再次進(jìn)行過濾處理
我們使用過濾器,也就是說,不止請求會經(jīng)過過濾器,我們的響應(yīng)也會經(jīng)過過濾器。
過濾器(Filter)接口
我們學(xué)習(xí)過濾器,肯定就要先看一下官方給我們提供的過濾器接口。下面我們使用Idea來查看Filter。
我們通過官方提供的過濾器可以看出過濾器(Filter)使用起來還是比較簡單的,下面我們就來學(xué)習(xí)如何使用過濾器(Filter)
使用過濾器(Filter)
我們使用過濾器肯定要導(dǎo)入相應(yīng)的jar包才行,F(xiàn)ilter就在servlet-api.jar中,我們將該jar包放到WEB-INF下的lib目錄下面,然后加入項(xiàng)目。
創(chuàng)建過濾器(Fliter)
我們創(chuàng)建Filter,只需要繼承Filter接口就行。
import javax.servlet.*; import java.io.IOException; public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { } }
Filter接口有3個方法,但是只有一個方法沒有實(shí)現(xiàn),我們只需要實(shí)現(xiàn)這個方法就行。我們可以發(fā)現(xiàn),我們實(shí)現(xiàn)了一個doFilter方法,這個方法就是我們寫過濾代碼的地方,具體邏輯就是和上面介紹的過濾器原理一樣的。
使用過濾器(Filter)
我們先來感受一下如何使用過濾器,細(xì)節(jié)我們后面慢慢說明。我們在上面創(chuàng)建的類中寫入以下代碼,并且加一個WebFIlter注解
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("對request進(jìn)行過濾"); //下面這行代碼就是放行 filterChain.doFilter(servletRequest,servletResponse); System.out.println("對response進(jìn)行過濾"); } }
我簡單介紹下上面的代碼,WebFilter("/*")表示對所有請求進(jìn)行過濾,而在doFilter中的放行代碼,也就是filterChain.doFilter(servletRequest,servletResponse);這行代碼就是對攔截進(jìn)行放行,細(xì)節(jié)我們后面講,現(xiàn)在先怎么理解就行。
啟動服務(wù)器,然后我們在游覽器中輸入http://localhost:8080/filter/abc,注意,filter是我們自己配置的web工程路徑,后面的abc隨便輸入的。我們下面來查看游覽器后控制臺輸出。
游覽器輸出
控制臺輸出
現(xiàn)在,我們就已經(jīng)可以得出兩個結(jié)論了,過濾器并不會管資源是否存在,而只會對配置的攔截路徑進(jìn)行攔截。攔截不僅會對請求進(jìn)行攔截,而且還會對相應(yīng)進(jìn)行攔截。
配置過濾器(Filter)攔截路徑
配置Filter的攔截路徑有2種方式,一種是注解,一種是xml方式,我們分別進(jìn)行講解。
注解方式
我們?nèi)绻褂米⒔鈦磉M(jìn)行配置,那么我們就需要使用@WebFilter,我們不說廢話,直接看該注解的源碼。
里面的配置項(xiàng)還是有很多的,下面我對常用配置項(xiàng)進(jìn)行說明:
- filterName:該filter的名字
- initParams:初始化參數(shù)
- displayName:filter顯示名稱
- servletNames:指定對哪些servlet進(jìn)行過濾
- asyncSupported:是否支持異步模式
- urlPatterns:指定攔截路徑
- value:指定攔截路徑
注意:urlPatterns和value是一樣的。urlPatterns和value只能配置一個,不能兩個都配置,兩個都配置就會報(bào)錯。
對于使用@WebFilter,里面的多個參數(shù)用 , 進(jìn)行分隔。
說明:如果我們僅僅需要配置一個攔截路徑,那么我們可以直接簡寫@WebLister("攔截路徑"),如@WebFilter("/*")就是攔截所有請求。
xml方式
xml方式可以說是和Servlet使用xml配置方式一樣了,這里就不廢話,直接配置一個。
<filter> <filter-name>myFilter</filter-name> <filter-class>com.clucky.filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
這個就是xml配置方式,只不過把注解換成了xml標(biāo)簽來配置,里面屬性都是一樣的,這個和Servlet的配置方式基本一樣,這里就不再贅述了。
過濾器(Filter)生命周期
我們都知道Servlet有一個生命周期,當(dāng)然Filter也有一個生命周期,下面我們就來探討一下Filter的生命周期。
Filter的生命周期和Servlet也十分相似,如果大家對Servlet的生命周期不怎么熟悉,那么可以看一下這篇文章Servlet生命周期。
我們創(chuàng)建一個類,實(shí)現(xiàn)Filter的所有方法。
import javax.servlet.*; import java.io.IOException; public class LifeCycleFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { } @Override public void destroy() { } }
理論說明
Filter有3個階段,分別是初始化,攔截和過濾,銷毀。
- 初始化階段:當(dāng)服務(wù)器啟動時,我們的服務(wù)器(Tomcat)就會讀取配置文件,掃描注解,然后來創(chuàng)建我們的Filter。
- 攔截和過濾階段:只要請求資源的路徑和攔截的路徑相同,那么過濾器就會對請求進(jìn)行過濾,這個階段在服務(wù)器運(yùn)行過程中會一直循環(huán)。
- 銷毀階段:當(dāng)服務(wù)器(Tomcat)關(guān)閉時,服務(wù)器創(chuàng)建的Filter也會隨之銷毀。
代碼演示
Filter的三個階段就對應(yīng)著Filter的3個方法,init方法會在Filter創(chuàng)建時調(diào)用,doFilter方法會在請求和攔截匹配時調(diào)用,destroy方法會在Filter銷毀時調(diào)用。我們來對這些方法進(jìn)行編寫驗(yàn)證。
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class LifeCycleFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { //這個方法就是初始化方法,在Filter創(chuàng)建時調(diào)用 System.out.println("調(diào)用了init()方法"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //這個方法就是過濾和攔截的方法,當(dāng)請求和攔截匹配時調(diào)用 System.out.println("調(diào)用了doFilter()方法"); } @Override public void destroy() { //這個方法就是銷毀方法,在Filter銷毀前調(diào)用 System.out.println("調(diào)用了destroy()方法"); } }
啟動服務(wù)器控制臺輸出
進(jìn)行攔截時控制臺輸出
關(guān)閉服務(wù)器控制臺輸出
都和我們預(yù)想的一樣,到此,我們就成功驗(yàn)證了Filter的生命周期。
FilterConfig和FilterChain說明
FilterConfig和FilterConfig這2個對象是由服務(wù)器(Tomcat)在創(chuàng)建和調(diào)用Filter對象時所傳入的,這2個對象十分有用,F(xiàn)ilterConfig對象可以讀取我們配置的初始參數(shù),F(xiàn)ilterChain可以實(shí)現(xiàn)多個Filter之間的連接。
FilterConfig
老規(guī)矩,我們要學(xué)習(xí)一個對象,首先查看類圖和源代碼
里面的方法就4個,下面我們分別進(jìn)行講解
- getFilterName():獲取filter的名稱
- getServletContext():獲取ServletContext
- getInitparamter(String var1):獲取配置的初始參數(shù)的值
- getInitParamterNames():獲取配置的所有參數(shù)名稱
FilterConfig實(shí)例運(yùn)用
我們在init方法中使用FilterConfig來讀取配置的數(shù)據(jù)庫的信息,然后輸出。
java代碼
import javax.servlet.*; import java.io.IOException; import java.util.Enumeration; public class MyFilterConfig implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("-----------獲取全部key:value------------"); //得到所有配置參數(shù)的名字 Enumeration<String> names = filterConfig.getInitParameterNames(); while (names.hasMoreElements()) { //得到每一個名字 String name = names.nextElement(); System.out.println(name+" = "+filterConfig.getInitParameter(name)); } System.out.println("-----------end.....------------"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { } @Override public void destroy() { } }
xml配置
<filter> <filter-name>myFilterConfig</filter-name> <filter-class>com.clucky.filter.MyFilterConfig</filter-class> <init-param> <param-name>driver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </init-param> <init-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/equip_employ_manage?serverTimezone=GMT</param-value> </init-param> <init-param> <param-name>username</param-name> <param-value>root</param-value> </init-param> <init-param> <param-name>password</param-name> <param-value>root</param-value> </init-param> </filter> <filter-mapping> <filter-name>myFilterConfig</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
啟動服務(wù)器,控制臺輸出
我們使用FilterConfig提供的方法就成功實(shí)現(xiàn)了功能,F(xiàn)ilterConfig就是用來讀取配置文件的。
FilterChain
一樣,我們還是先來查看源代碼以及類圖
我們查看類圖,可以發(fā)現(xiàn)FilterChain就只有一個方法,其實(shí)這個方法就是用來對攔截進(jìn)行放行的,如果有多個攔截器,那么就會繼續(xù)調(diào)用下一個Filter進(jìn)行攔截。doFilter方法需要傳入個參數(shù),一個是ServletRequest,一個是ServletResponse參數(shù),這個直接傳入進(jìn)行。
Tomcat在調(diào)用過濾器時,默認(rèn)就會傳入Request和Response,這個參數(shù)封裝了請求和響應(yīng),我們直接使用就行。ServletResquest和ServletResponse可以直接強(qiáng)轉(zhuǎn)成HttpServletRequest和HttpServletResponse,然后使用相應(yīng)的方法。
將ServletRequest轉(zhuǎn)成HttpServletRequest
FilterChain應(yīng)用實(shí)例
我們前面一直都是一個Filter,現(xiàn)在我們來配置2個Filter,通過FilterChain來進(jìn)行多個過濾。
第一個Filter
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class Filter01 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("調(diào)用過濾器01對請求進(jìn)行過濾~~~~"); //放行,如果還有過濾器,那么就執(zhí)行下一個過濾器 filterChain.doFilter(servletRequest,servletResponse); System.out.println("調(diào)用過濾器01對響應(yīng)進(jìn)行過濾~~~~"); } @Override public void destroy() { } }
第二個過濾器
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class Filter02 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("調(diào)用過濾器02對請求進(jìn)行過濾~~~~"); //放行,如果還有過濾器,那么就執(zhí)行下一個過濾器 filterChain.doFilter(servletRequest,servletResponse); System.out.println("調(diào)用過濾器02對響應(yīng)進(jìn)行過濾~~~~"); } @Override public void destroy() { } }
啟動服務(wù)器,然后我們游覽器輸入http://localhost:8080/filter/abc(filter是我配置的web工程路徑)來進(jìn)行訪問,查看控制臺輸出。
我們可以看見Filter01先進(jìn)行過濾,然后交給Filter02,然后訪問資源,然后Filter02對響應(yīng)進(jìn)行過濾,然后Filter01對響應(yīng)進(jìn)行過濾。圖示如下:
我們先使用Filter01對請求進(jìn)行過濾,那么很自然的,我們就是使用Filter02先對響應(yīng)進(jìn)行過濾。
多個Filter的執(zhí)行順序
上面我們配置了2個過濾器,那么我們怎么知道那個過濾器先執(zhí)行呢?其實(shí)大家可以直接使用代碼進(jìn)行驗(yàn)證,培養(yǎng)獨(dú)立思考的習(xí)慣,這里我就直接給出答案了。
- 如果我們是在web.xml中配置的過濾器,那么過濾器的執(zhí)行順序就是<filter-mapping>在web配置的順序,配置在上面那么就會先執(zhí)行。
- 如果我們是使用@WebFilter進(jìn)行配置的,那么執(zhí)行順序就是字符比較順序來執(zhí)行,例如有2個過濾器,一個是AFilter,一個是BFilter,那么AFilter就會先執(zhí)行。
- 如果注解和xml混用,那么在web.xml中配置的會先執(zhí)行。
執(zhí)行順序驗(yàn)證
我這里就驗(yàn)證第一條,也就是web.xml中配置的順序和<filter-mapping>順序一樣,其他大家感興趣自己驗(yàn)證。
xml配置順序 3->1->2
<filter> <filter-name>filter03</filter-name> <filter-class>com.clucky.filter.Filter03</filter-class> </filter> <filter-mapping> <filter-name>filter03</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>filter01</filter-name> <filter-class>com.clucky.filter.Filter01</filter-class> </filter> <filter-mapping> <filter-name>filter01</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>filter02</filter-name> <filter-class>com.clucky.filter.Filter02</filter-class> </filter> <filter-mapping> <filter-name>filter02</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Filter01
import javax.servlet.*; import java.io.IOException; public class Filter01 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("調(diào)用過濾器01對請求進(jìn)行過濾~~~~"); //放行,如果還有過濾器,那么就執(zhí)行下一個過濾器 filterChain.doFilter(servletRequest,servletResponse); System.out.println("調(diào)用過濾器01對響應(yīng)進(jìn)行過濾~~~~"); } @Override public void destroy() { } }
Filter02
import javax.servlet.*; import java.io.IOException; public class Filter02 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("調(diào)用過濾器02對請求進(jìn)行過濾~~~~"); //放行,如果還有過濾器,那么就執(zhí)行下一個過濾器 filterChain.doFilter(servletRequest,servletResponse); System.out.println("調(diào)用過濾器02對響應(yīng)進(jìn)行過濾~~~~"); } @Override public void destroy() { } }
Filter03
import javax.servlet.*; import java.io.IOException; public class Filter03 implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("調(diào)用過濾器03對請求進(jìn)行過濾~~~~"); //放行,如果還有過濾器,那么就執(zhí)行下一個過濾器 filterChain.doFilter(servletRequest,servletResponse); System.out.println("調(diào)用過濾器03對響應(yīng)進(jìn)行過濾~~~~"); } @Override public void destroy() { } }
我們啟動服務(wù)器,游覽器訪問,然后查看控制臺輸出是不是我們配置的3->1->2的順序
發(fā)現(xiàn)執(zhí)行順序果然是這樣,另外2個的驗(yàn)證大家感興趣可以自己驗(yàn)證,我這里就不驗(yàn)證了,如果嫌麻煩,那么記住就行了。
Filter應(yīng)用實(shí)例(實(shí)現(xiàn)敏感詞匯過濾)
我們學(xué)了那么多,現(xiàn)在來做一個實(shí)例,我們寫一個評論頁面,可以進(jìn)行評論,如果評論中含有我們定義的敏感詞匯,那么我們就進(jìn)行過濾,使用**來進(jìn)行代替。
代碼實(shí)現(xiàn)
jsp頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>評論</title> </head> <body> <h1>輸入評論內(nèi)容</h1> <form action="${pageContext.request.contextPath}/comment" method="post"> <textarea name="message" cols="30" rows="10"></textarea> <input type="submit" value="提交"> </form> <p >${requestScope.get("name")}<span style="color: red">${requestScope.get("comment")}</span></p> </body> </html>
Filter代碼
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import java.io.IOException; import java.util.ArrayList; import java.util.List; @WebFilter(servletNames = {"comment"},initParams = {@WebInitParam(name = "sensitiveWord", value = "zz")}) public class CommentFilter implements Filter { private List<String> sensitiveWords = new ArrayList<>(); @Override public void init(FilterConfig filterConfig) throws ServletException { //得到敏感詞匯 String word = filterConfig.getInitParameter("sensitiveWord"); //加入集合 sensitiveWords.add(word); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //設(shè)置編碼 servletRequest.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html;charset=utf-8"); //得到評論 String message = servletRequest.getParameter("message"); for (String sensitiveWord : sensitiveWords) { //對所有敏感詞匯進(jìn)行過濾 if (message.contains(sensitiveWord)){ //替換敏感詞匯 message = message.replace(sensitiveWord, "**"); } } //存入request域 servletRequest.setAttribute("comment",message); //放行 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }
Servlet代碼
import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; import java.util.HashSet; @WebServlet(name = "comment",value = "/comment") public class CommentServlet extends HttpServlet { //記錄評論敏感詞匯的ip private HashSet<String> hashSet = new HashSet<>(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String message = request.getParameter("message"); String comment = (String) request.getAttribute("comment"); if (message.equals(comment)){ System.out.println("沒有敏感詞匯....."); //設(shè)置名字 request.setAttribute("name","good boy:"); }else { //有敏感詞匯,記錄IP String localAddr = request.getLocalAddr(); System.out.println(localAddr); hashSet.add(localAddr); //設(shè)置名字 request.setAttribute("name","bad boy:"); } //轉(zhuǎn)發(fā)到comment.jsp頁面 request.getRequestDispatcher("/comment.jsp").forward(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
代碼驗(yàn)證
我們輸入http://localhost:8080/filter/comment.jsp,來訪問jsp頁面.
輸入評論內(nèi)容:你好?。?!!
下面顯示了我們的評論,我們再來輸入:你真是個zz
顯示你真是個**,我們的評論過濾就成功了。
總結(jié)
到此這篇關(guān)于一文詳解JavaWeb過濾器(Filter)的文章就介紹到這了,更多相關(guān)Java 過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家
相關(guān)文章
Spring Boot Redis客戶端遠(yuǎn)程操作實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring Boot Redis客戶端遠(yuǎn)程操作實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04SpringMVC 中配置 Swagger 插件的教程(分享)
下面小編就為大家分享一篇SpringMVC 中配置 Swagger 插件的教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12SpringCloudStream中的消息分區(qū)數(shù)詳解
這篇文章主要介紹了SpringCloudStream中的消息分區(qū)數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12使用sts工具、SpringBoot整合mybatis的詳細(xì)步驟
這篇文章主要介紹了使用sts工具、SpringBoot整合mybatis的詳細(xì)步驟,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04Spring配置動態(tài)數(shù)據(jù)源實(shí)現(xiàn)讀寫分離的方法
這篇文章主要介紹了利用Spring配置動態(tài)數(shù)據(jù)源實(shí)現(xiàn)讀寫分離的方法,文中通過示例代碼介紹的很詳細(xì),相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價值,藕需要的朋友可以一起學(xué)習(xí)學(xué)習(xí)。2017-01-01java實(shí)現(xiàn)一個簡單的網(wǎng)絡(luò)爬蟲代碼示例
這篇文章主要介紹了java實(shí)現(xiàn)一個簡單的網(wǎng)絡(luò)爬蟲代碼示例,還是挺不錯的,這里分享給大家,需要的朋友可以參考下。2017-11-11SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情
這篇文章主要介紹了SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08