day19 Servlet Filter

Author:相忠良
Email: ugoood@163.com
起始於:June 17, 2018
最後更新日期:June 18, 2018css

聲明:本筆記依據傳智播客方立勳老師 Java Web 的授課視頻內容記錄而成,中間加入了本身的理解。本筆記目的是強化本身學習所用。如有疏漏或不當之處,請在評論區指出。謝謝。
涉及的圖片,文檔寫完後,一次性更新。html

創建 day19 web工程。java

1. filter 入門與案例

Filter 簡介:
Filter也稱之爲過濾器,它是Servlet技術中最激動人心的技術,WEB開發人員經過Filter技術,對web服務器管理的全部web資源:例如Jsp, Servlet, 靜態圖片文件或靜態 html 文件等進行攔截,從而實現一些特殊的功能。例如實現URL級別的權限訪問控制、過濾敏感詞彙、壓縮響應信息等一些高級功能。
Servlet API中提供了一個Filter接口,開發web應用時,若是編寫的Java類實現了這個接口,則把這個java類稱之爲過濾器Filter。經過Filter技術,開發人員能夠實現用戶在訪問某個目標資源以前,對訪問的請求和響應進行攔截,以下所示:
1web

入門例子:
需求:攔截index.jsp瀏覽器

1.創建cn.wk.web.filter.FilterDemo1攔截器:緩存

public class FilterDemo1 implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("haha!!!");
        chain.doFilter(request, response); // 放行
        System.out.println("wowo!!!");
    }

    public void init(FilterConfig filterconfig) throws ServletException {}
    public void destroy() {}
}

2.在 web.xml 中配置攔截器:服務器

<filter>
  <filter-name>FilterDemo1</filter-name>
  <filter-class>cn.wk.web.filter.FilterDemo1</filter-class>
</filter>

<filter-mapping>
  <filter-name>FilterDemo1</filter-name>
  <url-pattern>/index.jsp</url-pattern>
</filter-mapping>

3.index.jsp:app

<head>
  <title>Filter入門</title>
</head>

<body>
  <% System.out.println("index!!!"); %>
</body>

控制檯輸出結果爲:jsp

haha!!!
index!!!
wowo!!!

觀察結果,瞭解到:
web 瀏覽器 -> web 服務器 -> filter -> index.jsp -> filter -> web 服務器 -> web 瀏覽器post

filter在開發中的常見應用:

  1. filter能夠目標資源執行以前,進行權限檢查,檢查用戶有無權限,若有權限則放行,如沒有,則拒絕訪問;
  2. filter能夠放行以前,對request和response進行預處理,從而實現一些全局性的設置;
  3. filter在放行以後,能夠捕獲到目標資源的輸出,從而對輸出做出相似於壓縮這樣的設置。

解決亂碼問題:
弄一個filter攔截全部資源:

<filter>
  <filter-name>FilterDemo1</filter-name>
  <filter-class>cn.wk.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
  <filter-name>FilterDemo1</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

在FilterDemo1這個filter裏寫:

public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {

  request.setCharacterEncoding("UTF-8");
  response.setCharacterEncoding("UTF-8");
  response.setContentType("text/html;charset=UTF-8");
  chain.doFilter(request, response); // 放行
}

這樣,全部資源全UTF-8編碼,完全解決亂碼問題。

Filter 鏈:
多個filter組成鏈,順序由 web.xml 文件中 filter-mapping 的順序來決定,以下:

filter-mapping 順序爲:

<filter-mapping>
  <filter-name>FilterDemo1</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>FilterDemo2</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

結果爲(即filter鏈裏的filter順序爲先 filter1 後 filter2):

filterdemo1以前!!!
filterdemo2以前!!!
index!!!
filterdemo2以後!!!
filterdemo1以後!!!

Filter 的生命週期:
filter 的生命週期和 web 應用是同樣的。
一個攔截器,服務器中只有1個攔截器對象。不一樣攔截器有與之對應的不一樣的攔截器對象。

注意到public void init(FilterConfig filterconfig) {}中的FilterConfig對象,這是初始化filter的一個對象。這個對象在web.xml中配置。當filter初始化時,web.xml中相應的配置會被自動讀取。以下:

<filter>
  <filter-name>FilterDemo1</filter-name>
  <filter-class>cn.wk.web.filter.FilterDemo1</filter-class>
  <init-param>
    <param-name>xxx</param-name>
    <param-value>yyy</param-value>
  </init-param>
</filter>

程序中輸出初始化參數:

public void init(FilterConfig filterconfig) throws ServletException {
  String value = filterconfig.getInitParameter("xxx");
  System.out.println(value);
}

當服務器啓動時,初始化該過濾器同時打印yyy。有些內容不想在程序中固定,就可經過這種配置的方式設置。

有時咱們想在 doFilter 方法中使用初始化參數,作法是:

  1. FilterDemo1中定義成員變量private FilterConfig config;
  2. 在 doFilter() 方法中寫入String value = this.config.getInitParameter("xxx");得到參數值。

2. Filter 案例

2.1 filter 解決亂碼問題

package cn.wk.web.filter.example;
public class CharacterEncodingFilter implements Filter {

    private FilterConfig config;
    private String defaultCharset = "UTF-8"; // <- 默認

    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {

        // 獲取要設置的字符集
        String charset = this.config.getInitParameter("charset");
        if (charset == null)
            charset = defaultCharset;

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        request.setCharacterEncoding(charset);
        response.setCharacterEncoding(charset);
        response.setContentType("text/html;charset" + charset);

        chain.doFilter(request, response);
    }

    public void destroy() {}
    public void init(FilterConfig arg0) throws ServletException {}
}

web.xml配置,還配置了初始化參數charset,以下:

<filter>
  <filter-name>CharacterEncodingFilter</filter-name>
  <filter-class>cn.wk.web.filter.example.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>charset</param-name>  <!--charset 設置-->
    <param-value>UTF-8</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CharacterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

這樣,該 web 應用之後不用再考慮亂碼問題,且可在 web.xml 文件中的過濾器中靈活設置 charset。

2.2 filter 控制不緩存 jsp 文件

常見應用2,故事:
因爲 jsp 顯示內容來源於 servlet,內容是動態的,因此緩存 jsp 文件沒意義。因此,如今要作一個不緩存jsp文件的過濾器。

控制不緩存的過濾器:

public void doFilter(ServletRequest req, ServletResponse resp,
    FilterChain chain) throws IOException, ServletException {

  HttpServletRequest request = (HttpServletRequest) req;
  HttpServletResponse response = (HttpServletResponse) resp;

  // 設定3頭,不留緩存
  response.setDateHeader("Expires", -1);
  response.setHeader("Cache-Control", "no-cache");
  response.setHeader("Pragma", "no-cache");

  chain.doFilter(request, response);
}

web.xml中cache-mapping的配置:

<filter-mapping>
  <filter-name>NoCacheFilter</filter-name>
  <url-pattern>*.jsp</url-pattern>
</filter-mapping>

2.3 filter 控制緩存某些文件

常見應用3:控制瀏覽器緩存頁面中的靜態資源的過濾器
場景:有些動態頁面中引用了一些圖片或css文件以修飾頁面效果,這些圖片和css文件常常是不變化的,因此爲減輕服務器的壓力,可使用 filter 控制瀏覽器緩存這些文件,以提高服務器的性能。

靈活的過濾器:

<filter>
  <filter-name>CacheFilter</filter-name>
  <filter-class>cn.wk.web.filter.example.CacheFilter</filter-class>
  <init-param>
    <param-name>css</param-name>
    <param-value>10</param-value> <!--css 緩存10分鐘-->
  </init-param>
  <init-param>
    <param-name>jpg</param-name>
    <param-value>1</param-value> <!--jpg 緩存1分鐘-->
  </init-param>
  <init-param>
    <param-name>js</param-name>
    <param-value>20</param-value> <!--js 緩存1分鐘-->
  </init-param>
</filter>

<!--同一個 filter,三種 mapping-->

<filter-mapping>
  <filter-name>CacheFilter</filter-name>
  <url-pattern>*.jpg</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>CacheFilter</filter-name>
  <url-pattern>*.css</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>CacheFilter</filter-name>
  <url-pattern>*.js</url-pattern>
</filter-mapping>

cn.wk.web.filter.example.CacheFilter代碼:

// 控制瀏覽器緩存的過濾器
public class CacheFilter implements Filter {

    private FilterConfig config;

    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        // 1. 獲取用戶想訪問的資源
        String uri = request.getRequestURI();

        // 2. 獲取該資源的緩存時間
        int expires = 0;
        if (uri.endsWith(".jpg")) {
            expires = Integer.parseInt(this.config.getInitParameter("jpg"));
        } else if (uri.endsWith(".js")) {
            expires = Integer.parseInt(this.config.getInitParameter("js"));
        } else {
            expires = Integer.parseInt(this.config.getInitParameter("css"));
        }

        response.setDateHeader("expires", System.currentTimeMillis() + expires
                * 60 * 1000);

        chain.doFilter(request, response);
    }

    public void init(FilterConfig filterconfig) throws ServletException {
        this.config = filterconfig;
    }
    public void destroy() {}
}

2.4 filter 實現用戶自動登錄

3. filter 映射細節

<filter-mapping>元素用於設置一個 Filter 所負責攔截的資源。

一個Filter攔截的資源可經過兩種方式來指定:Servlet 名稱資源訪問的請求路徑。

  • <filter-name>子元素用於設置filter的註冊名稱。該值必須是在<filter>元素中聲明過的過濾器的名字
  • <url-pattern>設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)
  • <servlet-name>指定過濾器所攔截的Servlet名稱。

<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,能夠是 REQUEST,INCLUDE,FORWARD和ERROR之一,默認REQUEST。用戶能夠設置多個 子元素用來指定 Filter 對資源的多種調用方式進行攔截。

一個資源被調用的方式共有四種:REQUEST, INCLUDE, FORWARD 和 ERROR。

例子,如圖:
2

上圖中表示:servlet forward(request, response) 到一個jsp頁面。此時,若作對jsp攔截器,控制緩存的話,就需在攔截器中設置<dispatcher>FORWARD</dispatcher>,才能觸發這個攔截器。

部署案例:

<filter-mapping>
  <filter-name>testFilter</filter-name>
  <url-pattern>/test.jsp</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>testFilter</filter-name>
  <url-pattern>/index.jsp</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
</filter-mapping>

4. 加強request - 徹底解決亂碼問題的 filter

下面代碼解決了包括post和get請求在內的全部亂碼問題。

  • 用到了包裝模式加強了 request;
  • 用到了 sun 提供的HttpServletRequestWrapper包裝類,避免本身一個個覆蓋不想加強的方法!

注意:request.setCharacterEncoding("UTF-8");只解決 post,不能解決 get 亂碼問題。
本案例經過重寫 getParameter() 方法,調整得到值得編碼從 iso8859-1 到 request.getCharacterEncoding() 方法所指定的編碼方式完成 get 請求方式下的亂碼解決問題。

//真正解決全站亂碼
public class CharacterEncodingFilter2 implements Filter {   
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        request.setCharacterEncoding("UTF-8");  // 只解決 post,不能解決 get
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        chain.doFilter(new MyRequest(request), response);   //request.getparameter("password");
    }

    /*
    1.寫一個類,實現與被加強對象相同的接口
    2.定義一個變量,記住被加強對象
    3.定義一個構造方法,接收被加強對象
    4.覆蓋想加強的方法
    5.對於不想加強的方法,直接調用被加強對象(目標對象)的方法
     */

    class MyRequest extends HttpServletRequestWrapper{

        private HttpServletRequest request;
        public MyRequest(HttpServletRequest request) {
            super(request);
            this.request = request;
    }

        public String getParameter(String name) {           
            String value = this.request.getParameter(name);
            if(!request.getMethod().equalsIgnoreCase("get")) {return value;}

            if(value==null){return null;}

            try {
                return value = new String(value.getBytes("iso8859-1"),
                        request.getCharacterEncoding());
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);}}}

    public void destroy() {}
    public void init(FilterConfig filterConfig) throws ServletException {}
}

不要忘記在 web.xml 中配置這個過濾器,配置方法略。

5. 未學習部分

3

之後再說。

相關文章
相關標籤/搜索