hualinux servlet 2.17:Filter過濾

目錄

一、知識點

1.1 簡介

Filter 的過濾過程

Filter 的基本工作原理

Filter 接口

FilterChain接口

映射 Filter

1.2 小結

1.2.1 Filter 是什麼?

1.2.2 如何創建一一個Filter,並把他跑起來

1.2.3 Filter 相關的API

1.2.4   元素:指定過濾器所攔截的資源被Servlet容器調用的方式

1.3 HttpServletRequestWrapper(瞭解)

二、例子

2.1 例1  原生Filter接口實現簡單的hello

2.1.1 所需文件

2.1.2 相關代碼

2.1.3 運行效果

2.2 例2 FilterConfig接口

2.3 例3 映射Filter

2.3.1 需求

2.3.2  所需文件

2.3.3 文件對應的代碼

2.3.4 效果

2.4 優化2.3例3 使用HttpFilter(沒有直接繼承HttpFilter)

2.5 優化2.3例3 使用HttpFilter(直接繼承HttpFilter類)

2.6  例4 映射 Filter dispatcher

2.6.1 所需要文件

2.6.2 相關代碼

2.6.3 效果

三、 Filter經典案例

3.1 經典應用1使瀏覽器不緩存頁面的過濾器

3.2 經典應用2 字符編碼的過濾器

3.2.1 所需文件

3.2.2 實現代碼

3.2.3 運行效果

3.3 經典應用3 檢測用戶是否登錄的過濾器

3.3.1 相關文件

3.3.2 實現代碼

3.3.3 效果


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.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纔行

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章