過濾器是Servlet的高級特性之一,也別把它想得那么高深,只不過是實現Filter接口的java類罷了!
首先,我們來看看過濾器究竟Web容器的哪處:

從上面的圖我們可以發現,當瀏覽器發送請求給服務器的時候,先執行過濾器,然后才訪問Web的資源。服務器響應Response,從Web資源抵達瀏覽器之前,也會途徑過濾器。
我們很容易發現,過濾器可以比喻成一張濾網。我們想想現實中的濾網可以做什么:在泡茶的時候,過濾掉茶葉。那濾網是怎么過濾茶葉的呢?規定大小的網孔,只要網孔比茶葉小,就可以實現過濾了!
引申在Web容器中,過濾器可以做:過濾一些敏感的字符串【規定不能出現敏感字符串】、避免中文亂碼【規定Web資源都使用UTF-8編碼】、權限驗證【規定只有帶session或Cookie的瀏覽器,才能訪問web資源】等等等,過濾器的作用非常大,只要發揮想象就可以有意想不到的效果
也就是說:當需要限制用戶訪問某些資源時、在處理請求時提前處理某些資源、服務器響應的內容對其進行處理再返回、我們就是用過濾器來完成的!
直接舉例子來說明吧:
如果我沒有用到過濾器:瀏覽器通過http請求發送數據給Servlet,如果存在中文,就必須指定編碼,否則就會亂碼!
jsp頁面提交中文數據給Servlet處理
<form action="${pageContext.request.contextPath}/Demo1" method="post"> <input type="text" name="username"> <input type="submit" value="提交"></form>Servlet沒有指定編碼的情況下,獲取得到的是亂碼
Servlet中如何解決中文亂碼問題,我的其他博文中有:http://blog.csdn.net/hon_3y/article/details/54632004
也就是說:如果我每次接受客戶端帶過來的中文數據,在Serlvet中都要設定編碼。這樣代碼的重復率太高了!!!!
有過濾器的情況就不一樣了:只要我在過濾器中指定了編碼,可以使全站的Web資源都是使用該編碼,并且重用性是非常理想的!
只要Java類實現了Filter接口就可以稱為過濾器!Filter接口的方法也十分簡單:

其中init()和destory()方法就不用多說了,他倆跟Servlet是一樣的。只有在Web服務器加載和銷毀的時候被執行,只會被執行一次!
值得注意的是doFilter()方法,它有三個參數(ServletRequest,ServletResponse,FilterChain),從前兩個參數我們可以發現:過濾器可以完成任何協議的過濾操作!
那FilterChain是什么東西呢?我們看看:

FilterChain是一個接口,里面又定義了doFilter()方法。這究竟是怎么回事啊??????
我們可以這樣理解:過濾器不單單只有一個,那么我們怎么管理這些過濾器呢?在Java中就使用了鏈式結構。把所有的過濾器都放在FilterChain里邊,如果符合條件,就執行下一個過濾器(如果沒有過濾器了,就執行目標資源)。
上面的話好像有點拗口,我們可以想象生活的例子:現在我想在茶杯上能過濾出石頭和茶葉出來。石頭在一層,茶葉在一層。所以茶杯的過濾裝置應該有兩層濾網。這個過濾裝置就是FilterChain,過濾石頭的濾網和過濾茶葉的濾網就是Filter。在石頭濾網中,茶葉是屬于下一層的,就把茶葉放行,讓茶葉的濾網過濾茶葉。過濾完茶葉了,剩下的就是茶(茶就可以比喻成我們的目標資源)
過濾器和Servlet是一樣的,需要部署到Web服務器上的。
<filter>用于注冊過濾器
<filter-name>用于為過濾器指定一個名字,該元素的內容不能為空。<filter-class>元素用于指定過濾器的完整的限定類名。<init-param>元素用于為過濾器指定初始化參數,它的子元素指定參數的名字,<param-value>指定參數的值。在過濾器中,可以使用FilterConfig接口對象來訪問初始化參數。<filter-mapping>元素用于設置一個Filter 所負責攔截的資源。
一個Filter攔截的資源可通過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑
<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping><filter-name>子元素用于設置filter的注冊名稱。該值必須是在元素中聲明過的過濾器的名字<url-pattern>設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)<servlet-name>指定過濾器所攔截的Servlet名稱。<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默認REQUEST。用戶可以設置多個<dispatcher> 子元素用來指定 Filter 對資源的多種調用方式進行攔截。子元素可以設置的值及其意義: - REQUEST:當用戶直接訪問頁面時,Web容器將會調用過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那么該過濾器就不會被調用。 - INCLUDE:如果目標資源是通過RequestDispatcher的include()方法訪問時,那么該過濾器將被調用。除此之外,該過濾器不會被調用。 - FORWARD:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那么該過濾器將被調用,除此之外,該過濾器不會被調用。 - ERROR:如果目標資源是通過聲明式異常處理機制調用時,那么該過濾器將被調用。除此之外,過濾器不會被調用。
上面的配置是“/*”,所有的Web資源都需要途徑過濾器
如果想要部分的Web資源進行過濾器過濾則需要指定Web資源的名稱即可!
上面已經說過了,過濾器的doFilter()方法是極其重要的,FilterChain接口是代表著所有的Filter,FilterChain中的doFilter()方法決定著是否放行下一個過濾器執行(如果沒有過濾器了,就執行目標資源)。

我們發現test.jsp(我們的目標資源)成功訪問到了,并且在服務器上也打印了字符串!
我們來試試把chain.doFilter(req, resp);這段代碼注釋了看看!

test.jsp頁面并沒有任何的輸出(也就是說,并沒有訪問到jsp頁面)。
直接看下面的代碼。我們已經知道了”準備放行“會被打印在控制臺上和test.jsp頁面也能被訪問得到,但“放行完成“會不會打印在控制臺上呢?
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("準備放行"); //執行這一句,說明放行(讓下一個過濾器執行,或者執行目標資源) chain.doFilter(req, resp); System.out.println("放行完成"); }答案也非常簡單,肯定會打印在控制臺上的。我們來看看:

注意,它的完整流程順序是這樣的:客戶端發送http請求到Web服務器上,Web服務器執行過濾器,執行到”準備放行“時,就把字符串輸出到控制臺上,接著執行doFilter()方法,Web服務器發現沒有過濾器了,就執行目標資源(也就是test.jsp)。目標資源執行完后,回到過濾器上,繼續執行代碼,然后輸出”放行完成“
我們再多加一個過濾器,看看執行順序。
過濾器1 System.out.println("過濾器1開始執行"); //執行這一句,說明放行(讓下一個過濾器執行,或者執行目標資源) chain.doFilter(req, resp); System.out.println("過濾器1開始完畢");過濾器2 System.out.println("過濾器2開始執行"); chain.doFilter(req, resp); System.out.println("過濾器2開始完畢");Servlet System.out.println("我是Servlet1");當我們訪問Servlet1的時候,看看控制臺會出現什么:

執行順序是這樣的:先執行FilterDemo1,放行,執行FilterDemo2,放行,執行Servlet1,Servlet1執行完回到FilterDemo2上,FilterDemo2執行完畢后,回到FilterDemo1上
注意:過濾器之間的執行順序看在web.xml文件中mapping的先后順序的,如果放在前面就先執行,放在后面就后執行!如果是通過注解的方式配置,就比較urlPatterns的字符串大小



我們直接把用戶名和密碼都放在了Cookie中,這是明文的。懂點編程的人就會知道你的賬號了。
于是乎,我們要對密碼進行加密!
Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + md5.md5(user.getPassword()));在過濾器中,加密后的密碼就不是數據庫中的密碼的。所以,我們得在Dao添加一個功能【根據用戶名,找到用戶】 public User find(String username) { List<User> userList = UserDB.getUsers(); //遍歷List集合,看看有沒有對應的username和password for (User user : userList) { if (user.getUsername().equals(username)) { return user; } } return null; }在過濾器中,比較Cookie帶過來的md5密碼和在數據庫中獲得的密碼(也經過md5)是否相同 //得到Cookie的用戶名和密碼 if (value != null) { String username = value.split("http://.")[0]; String password = value.split("http://.")[1]; //在Cookie拿到的密碼是md5加密過的,不能直接與數據庫中的密碼比較 UserDao userDao = new UserDao(); User user = userDao.find(username); //通過用戶名獲得用戶信息,得到用戶的密碼,用戶的密碼也md5一把 String dbPassword = md5.md5(user.getPassword()); //如果兩個密碼匹配了,就是正確的密碼了 if (password.equals(dbPassword)) { request.getSession().setAttribute("user", user); } }新聞熱點
疑難解答