目錄
1.2.4 元素:指定過濾器所攔截的資源被Servlet容器調用的方式
1.3 HttpServletRequestWrapper(瞭解)
2.4 優化2.3例3 使用HttpFilter(沒有直接繼承HttpFilter)
2.5 優化2.3例3 使用HttpFilter(直接繼承HttpFilter類)
servet Filter過濾在以後開發中,使用的頻率還有有點高的,尤其在安全方面,如spring security,所以有必要學習一下。
一、知識點
1.1 簡介
- Filter 的基本功能是對 Servlet 容器調用 Servlet 的過程進行攔截,從而在 Servlet 進行響應處理的前後實現一些特殊的功能。
- 在 Servlet API 中定義了三個接口類來開供開發人員編寫 Filter 程序:Filter, FilterChain, FilterConfig
- Filter 程序是一個實現了 Filter 接口的 Java 類,與 Servlet 程序相似,它由 Servlet 容器進行調用和執行
- Filter 程序需要在 web.xml 文件中進行註冊和設置它所能攔截的資源:Filter 程序可以攔截 Jsp, Servlet, 靜態圖片文件和靜態 html 文件
Filter 的過濾過程
Filter 的基本工作原理
- 當在 web.xml 中註冊了一個 Filter 來對某個 Servlet 程序進行攔截處理時,這個 Filter 就成了 Servlet 容器與該 Servlet 程序的通信線路上的一道關卡,該 Filter 可以對 Servlet 容器發送給 Servlet 程序的請求和 Servlet 程序回送給 Servlet 容器的相應進行攔截,可以決定是否將請求繼續傳遞給 Servlet 程序,以及對請求和相應信息是否進行修改
- 在一個 web 應用程序中可以註冊多個 Filter 程序,每個 Filter 程序都可以對一個或一組 Servlet 程序進行攔截。
- 若有多個 Filter 程序對某個 Servlet 程序的訪問過程進行攔截,當針對該 Servlet 的訪問請求到達時,web 容器將把這多個 Filter 程序組合成一個 Filter 鏈(過濾器鏈)。Filter 鏈中各個 Filter 的攔截順序與它們在應用程序的 web.xml 中映射的順序一致
Filter 接口
- init(FilterConfig filterConfig)throws ServletException:在 web 應用程序啓動時,web 服務器將根據 web.xml 文件中的配置信息來創建每個註冊的 Filter 實例對象,並將其保存在服務器的內存中。Web容器創建 Filter 對象實例後,將立即調用該 Filter 對象的 init 方法。Init 方法在 Filter 生命週期中僅執行一次,web 容器在調用 init 方法時,會傳遞一個包含 Filter 的配置和運行環境的 FilterConfig 對象(FilterConfig的用法和ServletConfig類似)。利用FilterConfig對象可以得到ServletContext對象,以及部署描述符中配置的過濾器的初始化參數。在這個方法中,可以拋出ServletException異常,通知容器該過濾器不能正常工作。
- destroy():在Web容器卸載 Filter 對象之前被調用。該方法在Filter的生命週期中僅執行一次。在這個方法中,可以釋放過濾器使用的資源
- 與開發Servlet不同的是,Filter接口並沒有相應的實現類可供繼承,要開發過濾器,只能直接實現Filter接口。
- doFilter(ServletRequest request,ServletResponse response,
FilterChain chain)throws java.io.IOException,ServletException:
doFilter()方法類似於Servlet接口的service()方法。當客戶端請求目標資源的時候,容器就會調用與這個目標資源相關聯的過濾器的doFilter()方法。其中參數 request, response 爲 web 容器或 Filter 鏈的上一個 Filter 傳遞過來的請求和相應對象;參數 chain 爲代表當前 Filter 鏈的對象,在特定的操作完成後,可以在當前 Filter 對象的 doFilter 方法內部需要調用 FilterChain 對象的 chain.doFilter(request,response)方法才能把請求交付給 Filter 鏈中的下一個 Filter 或者目標 Servlet 程序去處理,也可以直接向客戶端返回響應信息,或者利用RequestDispatcher的forward()和include()方法,以及HttpServletResponse的sendRedirect()方法將請求轉向到其他資源。這個方法的請求和響應參數的類型是ServletRequest和ServletResponse,也就是說,過濾器的使用並不依賴於具體的協議。
FilterChain接口
- FilterChain接口:代表當前 Filter 鏈的對象。由容器實現,容器將其實例作爲參數傳入過濾器對象的doFilter()方法中。過濾器對象使用FilterChain對象調用過濾器鏈中的下一個過濾器,如果該過濾器是鏈中最後一個過濾器,那麼將調用目標資源。
- doFilter(ServletRequest request,ServletResponse response)throws java.io.IOException:調用該方法將使過濾器鏈中的下一個過濾器被調用。如果是最後一個過濾器,會調用目標資源。
- javax.servlet.FilterConfig接口:該接口類似於ServletConfig接口,由容器實現。Servlet規範將代表 ServletContext 對象和 Filter 的配置參數信息都封裝在該對象中。Servlet 容器將其作爲參數傳入過濾器對象的init()方法中。
- String getFilterName():得到描述符中指定的過濾器的名字。
- String getInitParameter(String name): 返回在部署描述中指定的名字爲name的初始化參數的值。如果不存在返回null.
- Enumeration getInitParameterNames():返回過濾器的所有初始化參數的名字的枚舉集合。
- public ServletContext getServletContext():返回Servlet上下文對象的引用。
映射 Filter
- <filter-mapping>元素用於設置一個 Filter 所負責攔截的資源。一個Filter攔截的資源可通過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑( url樣式)
- <filter-name>子元素用於設置filter的註冊名稱。該值必須是在<filter>元素中聲明過的過濾器的名字
- <url-pattern>設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)
- <servlet-name>指定過濾器所攔截的Servlet名稱。
- <dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默認REQUEST. 可以設置多個<dispatcher> 子元素用來指定 Filter 對資源的多種調用方式進行攔截
- <dispatcher> 子元素可以設置的值及其意義:
- REQUEST:當用戶直接訪問頁面時,Web容器將會調用過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那麼該過濾器就不會被調用。
- INCLUDE:如果目標資源是通過RequestDispatcher的include()方法訪問時,那麼該過濾器將被調用。除此之外,該過濾器不會被調用。
- FORWARD:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那麼該過濾器將被調用,除此之外,該過濾器不會被調用。
- ERROR:如果目標資源是通過聲明式異常處理機制調用時,那麼該過濾器將被調用。除此之外,過濾器不會被調用。
- 在同一個 web.xml 文件中可以爲同一個 Filter 設置多個映射。若一個 Filter 鏈中多次出現了同一個 Filter 程序,這個 Filter 程序的攔截處理過程將被多次執行
1.2 小結
1.2.1 Filter 是什麼?
1.JavaWEB的一個重要組件, 可以對發送到Servlet的請求進行攔截,並對響應也進行攔截。
2.Filter是實現了Filter接口的Java 類。
3.Filter需要在web. xml文件中進行配置和映射。
1.2.2 如何創建一一個Filter,並把他跑起來
1.創建一個Filter類:實現Filter接口: public class HelloFilter implements Filter
2.在web.xml文件中配置並映射讀Filter.其中url-pattern指定該Filter可以攔截可些資源,即可以通過哪些url訪問到該Filter<!--註冊Filter --> <filter> <filter-name>helloFilter</filter-nane> <filter-class>com.hualinux.javaweb.HelloF11ter</fllter-class> </filter> <!--映射Filter --> <filter-mapping> <filter-name>helloFilter</fi1ter-name> <ur1-pattern>/test.jsp</url-pattern> </filter-mapping>
1.2.3 Filter 相關的API
Filter 相關的API:
1.Filter接口:
public void init(FilterConfig fllterConfig): 類似於Servlet 的init方法。在創建Fllter 對象(Filter 對象
立即被調用,且只被調用-次。該方法用於對當前的Filter進行初始化操作。Filter 實例是單例的。.
* FilterConfig類似於ServletConfig
* 可以在web.xml文件中配置當前Filter的初始化參數。配置方式也和Servlet類似。
<filter>
<filter-name>helloF ilter</filter- name>
<filter-class>com.hualinux.javaweb.HelloFilter</fi1ter-class>
<init-param>
<param- name>name</param- name>
<param-value> root</param-value>
</init-param>
</filter>public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 真正Filter的邏輯代碼需要編寫在該方法中。每次攔截都會謂用該方法。
*FilterChain: Filter 鏈。多個Filter可以構成- 個Filter鏈
-doFilter(Serv1etRequest request, ServletResponse response):把請求傳給Filter錯的下一個Filte若當前Filter是Filter錯的最後一個Filter,將把請求給到目標Serlvet(或JSP)
Filter
-多個Fi1ter 攔截的順序和<filter- mapping> 配置的順序有關,靠樹的先被調用。public void destroy(): 釋放當前Filter所佔用的資源的方法。在Fi1ter被銷燬之前被調用,且只被調用一次。
1.2.4 <dispatcher> 元素:指定過濾器所攔截的資源被Servlet容器調用的方式
可以是REQUEST, INCLUDE , FORWARD和ERROR之-.默認REQUEST.
可以設置多個<dispatcher>子元用來指定Filter對資源的多種調用方式進行攔截
1. REQUEST. 當用戶直接訪問頁面時,Web容器將公調用過濾器。如果目標資源是通過RequestDispatcher的include( )或forward( )方法訪間,通過GET或POST請求直接訪問。
2.FORHARD. 如果目標資源是通過RequestDispatcher的forward( )方法訪問時,那麼該過濾器將被調用。除此之外,該過濾器不會被調用。或<jsp:forward page-"/.." />或通過page指令的errorPage轉發頁面. <X@ page errorPage="test.jsp" %>
3. INCLUDE. 如果目標資源是通過RequestDispatcher的include( )方法訪問時。那麼資過濾器將被調用。除此之外。該過濾器不會被調用。或<jsp:include file="/..." />
4. ERROR. 如果目標資源是通過聲明式異常處理機制調用時。那麼該過濾器將被調用。除此之外。過濾器不會被調用。
在web. xml文件中通過error-page節點進行聲明:
<error-page> <exception-type>java.lang.ArithmeticException</ exception-type> <location>/test.jsp</location> </error-page> </filter-mapping> <filter-name>secondFilter</filter- name> <url-pattern>/test .jsp</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher> FORWARD</dispatcher> <dispatcher> INCLUDE</dispatcher> <dispatcher> ERROR</ dispatcher> </fi1ter-mapping>
1.3 HttpServletRequestWrapper(瞭解)
1.. why:
需要改變從Servlet容器(可能是任何的Serlvet 容器)中傳入的HttpServletReuqest對象的某個行爲,該怎麼辦?
1).繼承HttpServletReuqest接口的Servlet容器的實現類,但就和具體的容器相耦合了
2).提供HttpServletRequest接口的實現類,很麻煩,而且也需要和具體的容器相耦合
3).使用裝飾器設計模式:
>提供-一個類,該類實現HttpServletRequest接口
>傳入具體的一一個容器實現的HttpServletRequest接口的實現類作爲上述類的一個成員變量
>使用HttpServletRequest成員變量來實現HttpServletRequest接口的所有方法public class ServletRequestWrapper implements ServletRequest { //被包裝的那個ServletRequest 對象 private ServletRequest request; //構造器傳入ServletRequest實現類對象 public ServletRequestWrapper(ServletRequest request) { if (request == nul) { throw new llegalArgumentException("Request cannot be null'); } this.request = request; //具體實現ServletRequest的方法:調用被包裝的那個成員變量的方法實現。 public Object getAttribute(String name) { return this.request.getAttribute(name); } public Enumeration getAttributeNames0 { return this.requst.gettributeNames0; } //...
>再提供上述類的實現類,重寫具體的方法即可。
public class MyHttpServletRequest extends HttpServletRequestWrapper{ public MyHttpServletRequstHttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String val = super.getParameter(name); if(val != null && val.contains(" fuck ")){ val = val.replace("fuck", *****); } return val; } }
二、例子
2.1 例1 原生Filter接口實現簡單的hello
2.1.1 所需文件
2.1.2 相關代碼
1.在web下建立index.html內容爲:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<a href="test.jsp">To Test Page</a>
</body>
</html>
2. 在web下建立test.jsp內容爲:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head>
<title>test</title>
<meta charset="UTF-8">
</head>
<body>
<%
out.println("this is Test");
%>
</body>
</html>
3. 在src--> com.hualinux.filter1--> HelloFilter.java
ackage com.hualinux.filter1;
import javax.servlet.*;
import java.io.IOException;
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
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() {
System.out.println("destroy...");
}
}
4. web-->WEB-INF-->web.xml代碼
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 註冊Filter -->
<filter>
<filter-name>helloFilter</filter-name>
<filter-class>com.hualinux.filter1.HelloFilter</filter-class>
</filter>
<!-- 映射Filter -->
<filter-mapping>
<filter-name>helloFilter</filter-name>
<!-- 配置要攔截的目標,可以結合*號指定後綴文件,如*.jsp、/* -->
<url-pattern>/test.jsp</url-pattern>
</filter-mapping>
</web-app>
2.1.3 運行效果
注:我這裏使用的是360瀏覽器,我把tomcat也修改了一下根目錄直接爲locahost:8080/
發現網頁空白
上圖中之所以空白是被配置的Fitler給攔截了,所以test.jsp沒有出內容,刷新幾次上頁面
發現每刷新1次,Filter的doFilter就會被調用1次,init只執行一次,這和前面serlvet很像.
按停止,則destroy方法纔會被調用
如果要讓test.jsp顯示內容怎麼辦,可以使用FilterChain放行
doFilter(ServletRequest request,ServletResponse response)throws java.io.lOException :調用該方法將使
過濾器鏈中的下一個過濾器被調用。如果是最後一個過濾器,會調用目標資源。
在src--> com.hualinux.filter1--> HelloFilter.java中的doFilter()方法添加
filterChain.doFilter(servletRequest, servletResponse);
如下圖:
再次運行,效果:
從上圖來看test.jsp中的內容被顯示出來了。
2.2 例2 FilterConfig接口
FilterConfig和servletConfig很像,這裏就不再寫了,要學會“舉一反三”,思想遷移
只學會了技術,當來了新技術之後一點思路都沒有,那是挻失敗的事。
2.3 例3 映射Filter
2.3.1 需求
2.3.2 所需文件
所需要的文件如下:紅色方框
2.3.3 文件對應的代碼
1. WEB-->login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登錄</title>
</head>
<body>
<span style="color: red">${msg}</span>
<br><br>
<form action="hello.jsp" method="post">
username:<input type="text" name="username" />
password:<input type="password" name="pwd" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
2. WEB-->hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>hello</title>
</head>
<body>
Hello:${param.username}
</body>
</html>
3. src--> com.hualinux.filter1--> UserNameFilter.java
package com.hualinux.filter1;
import javax.servlet.*;
import java.io.IOException;
public class UserNameFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String initUser = filterConfig.getInitParameter("username");
String username = servletRequest.getParameter("username");
if(!initUser.equals(username)){
servletRequest.setAttribute("msg","用戶名不正確");
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
4. src--> com.hualinux.filter1--> PasswordFilter.java
package com.hualinux.filter1;
import javax.servlet.*;
import java.io.IOException;
public class PasswordFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ;
String initPassword = filterConfig.getServletContext().getInitParameter("pwd");
System.out.println("web.xml的pwd是:"+initPassword);
String password = servletRequest.getParameter("pwd");
if(!initPassword.equals(password)){
servletRequest.setAttribute("msg","密碼不正確");
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
5. web-->WEB-INF-->web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>pwd</param-name>
<param-value>1234</param-value>
</context-param>
<!-- 註冊Filter -->
<filter>
<display-name>UserNameFitler</display-name>
<filter-name>UserNameFitler</filter-name>
<filter-class>com.hualinux.filter1.UserNameFilter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>Tom</param-value>
</init-param>
</filter>
<!-- 映射Filter -->
<filter-mapping>
<filter-name>UserNameFitler</filter-name>
<url-pattern>/hello.jsp</url-pattern>
</filter-mapping>
<filter>
<display-name>PasswordFitler</display-name>
<filter-name>PasswordFitler</filter-name>
<filter-class>com.hualinux.filter1.PasswordFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PasswordFitler</filter-name>
<url-pattern>/hello.jsp</url-pattern>
</filter-mapping>
</web-app>
2.3.4 效果
運行,打開瀏覽器輸入:http://localhost:8080/hello.jsp
輸入正確密碼的提示:
2.4 優化2.3例3 使用HttpFilter(沒有直接繼承HttpFilter)
和Servlet一樣,當我們使用到http的時候,每次也要強轉一下,
當需要使用到Http相關操作的時候,經常會用到GET或POST,每次都得把HttpServletRequest或HttpServletResponse方法進行強轉
如下面代碼:
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
太麻煩了,用到http方面的,可以直接使用Filter 接口子類HttpFilter
建立src--> com.hualinux.filter1-->HttpFilter.java,代碼如下:
package com.hualinux.filter1;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public abstract class HttpFilter implements Filter {
private FilterConfig filterConfig;
/*
* 不建議子類直接覆蓋。 若直接覆蓋,將可能會導致 filterConfig 成員變量初始化失敗
* */
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig=filterConfig;
init();
}
/*
* 供子類繼承的初始化方法。 可以通過 getFilterConfig() 獲取 filterConfig 對象.
* */
protected void init(){}
/*
* 原生的 doFilter 方法,在方法內部把 ServletRequest 和 ServletResponse 轉爲了 HttpServletRequest 和 HttpServletResponse
* 並調用了doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
*
* 若編寫 Filter 的過濾方法不建立直接繼承該方法。而是建議繼承
* doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 方法
* */
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
doFilter(request, response, filterChain);
}
/*
* 抽象方法,爲 Http 請求定製,必須實現方法。
* @param request
* @param response
* @param filterChain
* @throws IOException
* @throws ServletException
* */
public abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException;
/*
* 直接返回init(ServetConfig) 的 FilterConfig 對象
* */
public FilterConfig getFilterConfig(){
return filterConfig;
}
@Override
public void destroy() {
}
}
2.5 優化2.3例3 使用HttpFilter(直接繼承HttpFilter類)
上面的2.4 優化還需要自己寫,其實有現成的HttpFilter,直接使用即可。只有2個方法
具體代碼修改在這裏我不寫了,以爲和serlvet的相似
2.6 例4 映射 Filter dispatcher
此例子是 “2.1 例1”的基礎上稍微修改一下的,其中web.xml、HelloFilter、test.jsp不變
在index.hml和test.jsp中添加一個文件轉而已,修改前後圖如下:
2.6.1 所需要文件
2.6.2 相關代碼
1.index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<a href="dispatcher.jsp">To Test Page</a>
</body>
</html>
2. dispatcher.jsp代碼
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>dispatcher</title>
</head>
<body>
<jsp:forward page="/test.jsp"></jsp:forward>
</body>
</html>
3. web.xml代碼不變
<!-- 註冊Filter -->
<filter>
<filter-name>helloFilter</filter-name>
<filter-class>com.hualinux.filter1.HelloFilter</filter-class>
</filter>
<!-- 映射Filter -->
<filter-mapping>
<filter-name>helloFilter</filter-name>
<!-- 配置要攔截的目標,可以結合*號指定後綴文件,如*.jsp、/* -->
<url-pattern>/test.jsp</url-pattern>
</filter-mapping>
2.6.3 效果
沒點下面之前清空ide最下方的信息
發現最下方Filter沒有被調用,沒有doFilter
分析原因:
<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默認REQUEST. 可以設置多個<dispatcher> 子元素用來指定 Filter 對資源的多種調用方式進行攔截
因爲我們用的是forward方式,並不是REQUEST,所以需要在web.xml配置一個<dispatcher>子元素用來指定 Filter
打開web.xml,修改如下:
<!-- 映射Filter -->
<filter-mapping>
<filter-name>helloFilter</filter-name>
<!-- 配置要攔截的目標,可以結合*號指定後綴文件,如*.jsp、/* -->
<url-pattern>/test.jsp</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
添加一個<dispatcher>子節點就行了,重啓tomcat,清空ide最下方信息,再次測試一下
發現有“doFilter...”了,如下圖:
同理如果用到其它方式相繼添加<dispatcher>子節點就行了
三、 Filter經典案例
3.1 經典應用1使瀏覽器不緩存頁面的過濾器
- 使瀏覽器不緩存頁面的過濾器:
- 有 3 個 HTTP 響應頭字段都可以禁止瀏覽器緩存當前頁面,它們在 Servlet 中的示例代碼如下:
- response.setDateHeader("Expires",-1);
- response.setHeader("Cache-Control","no-cache");
- response.setHeader("Pragma","no-cache");
- 並不是所有的瀏覽器都能完全支持上面的三個響應頭,因此最好是同時使用上面的三個響應頭
- 有 3 個 HTTP 響應頭字段都可以禁止瀏覽器緩存當前頁面,它們在 Servlet 中的示例代碼如下:
具體代碼我這裏不寫了,可以看相關視頻截圖,這個應用得少。
3.2 經典應用2 字符編碼的過濾器
3.2.1 所需文件
所需要的文件很簡單,就是在web目錄下建立一個encoding目錄,再建立a.jsp和b.jsp文件
3.2.2 實現代碼
web-->encoding-->a.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>a</title>
<meta charset="UTF-8">
</head>
<body>
<form action="b.jsp" method="post">
name:<input type="text" name="name" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
web-->encoding-->b.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>b</title>
<meta charset="UTF-8">
</head>
<body>
Hello:${param.name}
</body>
</html>
3.2.3 運行效果
打開瀏覽器,輸入:http://localhost:8080/encoding/a.jsp
輸入abc提交,效果如下:
但是輸入中文,如“廣州”,會產生亂碼,如下圖:
解決方法一:
在請求中指定編碼,我這裏使用的是UTF-8,b.jsp修改如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>b</title>
<meta charset="UTF-8">
</head>
<body>
<%
request.setCharacterEncoding("UTF-8");
%>
Hello:${param.name}
</body>
</html>
重啓tomcat再次測試,顯示正常
這裏有一個問題,如果我的頁面是幾百上千萬個,我每個頁都添加這段代碼,會累死的!
而且還不好維護...所以使用過濾器統一過濾這樣比較好
思路:
爲了方便統一修改編碼,我樣可以放在一個配置文件中,這裏可以放以web.xml中
然後通過filter統一過渡
實現:
1. 把b.jsp添加的請求編碼去掉:
<%
request.setCharacterEncoding("UTF-8");
%>
恢復到起始的狀態
2. web.xml添加編碼,如下:
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.hualinux.filter1.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/encoding/*</url-pattern>
</filter-mapping>
3. src-->com.hualinux.filter1-->EncodingFilter.java
這裏用到前面的手工寫的HttpFilter類
ackage com.hualinux.filter1;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class EncodingFilter extends HttpFilter{
private String encoding;
@Override
protected void init() {
encoding= getFilterConfig().getServletContext().getInitParameter("encoding");
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
request.setCharacterEncoding(encoding);
filterChain.doFilter(request,response);
}
}
4. 運行一下試下效果和之前的一樣不
3.3 經典應用3 檢測用戶是否登錄的過濾器
檢測用戶是否登陸的過濾器:
-情景:系統中的某些頁面只有在正常登陸後纔可以使用,用戶請求
這些頁面時要檢查session中有無該用戶信息,但在所有必要的頁
面加上session的判斷相當麻煩的事情-解決方案:編寫一個用於檢測用戶是否登陸的過濾器,如果用戶未
登錄,則重定向到指的登錄頁面-要求:需檢查的在Session中保存的關鍵字;如果用戶未登錄,需
重定向到指定的頁面(URL不包括ContextPath);不做檢查的URL列
表(以分號分開,並且URL中不包括ContextPath)都要採取可配置
的方式
3.3.1 相關文件
其中HttpFilter代碼上面有,不再寫
3.3.2 實現代碼
1. web-->login-->a.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>a</title>
</head>
<body>
<h3>AAA PAGE</h3>
<a href="list.jsp">Return...</a>
</body>
</html>
其中b.jsp、c.jsp、d.jsp、e.jsp的
<a href="list.jsp">Return...</a>
是不變的,還有把a.jsp相應地做出修改
<h3>AAA PAGE</h3>
把它分別修改成
<h3>BBB PAGE</h3>
<h3>CCC PAGE</h3>
<h3>DDD PAGE</h3>
<h3>EEE PAGE</h3>
2. web-->login-->list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>list</title>
</head>
<body>
<a href="a.jsp">AAA</a>
<br><br>
<a href="b.jsp">BBB</a>
<br><br>
<a href="c.jsp">CCC</a>
<br><br>
<a href="d.jsp">DDD</a>
<br><br>
<a href="e.jsp">EEE</a>
<br><br>
</body>
</html>
3. web-->login-->login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login</title>
</head>
<body>
<form action="doLogin.jsp" method="post">
username:<input type="text" name="username" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
4. web-->WEB-INF-->web.xml
<!-- 用戶信息放入到 sesskon中的鍵的名字-->
<context-param>
<param-name>userSessionKey</param-name>
<param-value>USERSESSIONKEY</param-value>
</context-param>
<!-- 右未登錄,需重寫向頁面-->
<context-param>
<param-name>rediretPage</param-name>
<param-value>/login/login.jsp</param-value>
</context-param>
<!-- 不需要攔截(或檢查)的 URL 列表 -->
<context-param>
<param-name>uncheckedUrls</param-name>
<param-value>/login/a.jsp,/login/list.jsp,/login/login.jsp,/login/doLogin.jsp</param-value>
</context-param>
<filter>
<filter-name>lgoinFitler</filter-name>
<filter-class>com.hualinux.filter1.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>lgoinFitler</filter-name>
<url-pattern>/login/*</url-pattern>
</filter-mapping>
5. web-->login-->doLogin.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登陸</title>
</head>
<body>
<%
//1. 獲取用戶的登陸信息
String username = request.getParameter("username");
//2. 若登陸信息完整,則把登陸信息放到 HttpSession
if (username != null && !username.trim().equals("")){
session.setAttribute(application.getInitParameter("userSessionKey"),username);
//3. 重定向到list.jsp
response.sendRedirect("list.jsp");
}else {
response.sendRedirect("login.jsp");
}
%>
</body>
</html>
6. src--> com.hualinux.filter1-->LoginFilter.java
package com.hualinux.filter1;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class LoginFilter extends HttpFilter {
// 1. 從web.xml 文件中獲取 sessionKey,redirectUrl, uncheckedUrls
private String sessionKey;
private String redirectUrl;
private String uncheckedUrls;
@Override
protected void init() {
ServletContext servletContext = getFilterConfig().getServletContext();
sessionKey = servletContext.getInitParameter("userSessionKey");
redirectUrl = servletContext.getInitParameter("rediretPage");
uncheckedUrls = servletContext.getInitParameter("uncheckedUrls");
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
//1. 獲取請求的URL
// String requestURL = request.getRequestURL().toString();
// String requestURI = request.getRequestURI();
// /login/b.jsp
String servletPath = request.getServletPath();
//2. 檢查1獲取的 servletPath 是否爲不需要檢查的 URL 中的一個,若是,則直接放行,方法結束
List<String> urls = Arrays.asList(uncheckedUrls.split(","));
if(urls.contains(servletPath)){
filterChain.doFilter(request,response);
return;
}
//3.從 session 中獲取 sessionKey 對應的值,若值不存在,則重定向到 redirectUrl
Object user = request.getSession().getAttribute(sessionKey);
if(user == null){
response.sendRedirect(request.getContextPath()+redirectUrl);
return;
}
//4. 右存在,則放行,充許訪問
filterChain.doFilter(request,response);
}
}
3.3.3 效果
運行,打開瀏覽器輸入http://localhost:8080/login/list.jsp
點BBB效果
因爲上圖沒有編寫登出代碼,需要清空cookie纔行