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