天天看點

JavaWeb——Filter過濾器1、Filter可以做什麼2、Filter如何實作攔截3、Filter開發步驟4、Filter的生命周期5、Decorator設計模式6、Filter的應用

1、Filter可以做什麼

Filter(過濾器)對web伺服器管理的web資源進行攔截,進而實作一些特殊功能。例如實作URL級别的權限通路控制、過濾敏感詞彙、壓縮響應資訊等一些功能。

2、Filter如何實作攔截

web浏覽器<————>web伺服器<————>過濾器<————>web資源

Filter接口中有一個doFilter方法,web伺服器在調用doFilter方法時,會傳遞一個filterChain對象,該對象有一個doFilter()方法,如果調用改方法,則伺服器調用web資源,否則web資源不會被通路。

Filter接口的doFilter()方法:

  • 前:對request,response做預處理
  • 是否調用目标資源,是否執行filterChain對象的diFilter()方法。
  • 後:捕獲響應的web資源,實作一些新的功能。

3、Filter開發步驟

Filter開發分為兩個步驟,首先要編寫java類實作filter接口和doFilter方法,二是在web.xml中對編寫的filter接口進行注冊和映射,設定其攔截的資源。

3.1 XML配置

3.1.1 filter注冊

注冊Filter範例:

<filter>
      <description>FilterDemo02過濾器</description>
      <filter-name>FilterDemo02</filter-name>
      <filter-class>me.gacl.web.filter.FilterDemo02</filter-class>
      <!--配置FilterDemo02過濾器的初始化參數-->
      <init-param>
          <description>配置FilterDemo02過濾器的初始化參數</description>
          <param-name>name</param-name>
          <param-value>gacl</param-value>
      </init-param>
      <init-param>
          <description>配置FilterDemo02過濾器的初始化參數</description>
          <param-name>like</param-name>
          <param-value>java</param-value>
      </init-param>
</filter>
           
  • <filter>

    标簽用于内注冊一個filter過濾器
  • <description>

    标簽用于添加描述資訊(可省略)
  • <filter-name>

    标簽用于指定過濾器的名稱
  • <filter-class>

    标簽用于指定過濾器的類名
  • <init-param>

    标簽用于設定filter的初始化參數,

    <param-name>

    <param-value>

    制定了某屬性的名稱及該屬性的值。

<init-param>

标簽中的屬性可以通過FilterConfig對象的方法獲得。

3.1.2 filter映射

映射filter執行個體1:

<!--映射過濾器-->
  <filter-mapping>
      <filter-name>FilterDemo02</filter-name>
      <!--“/*”表示攔截所有的請求 -->
      <url-pattern>/*</url-pattern>
  </filter-mapping>
           

映射filter執行個體2:

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

<dispatcher>

标簽設定使用者以什麼方式通路時,過濾器工作。标簽的屬性值:

  1. REQUEST:使用者直接通路頁面,過濾器工作。
  2. INCLUDE:目标資源通過RequestDispatcher的include()方法通路,過濾器工作。
  3. FORWARD:目标資源通過RequestDispatcher的forward()方法通路,過濾器工作。
  4. ERROR:目标資源通過聲明式異常處理機制調用時,過濾器工作。

3.2 實作Filter接口并實作doFilter方法

過濾器範例:

public class FilterDemo01 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("----過濾器初始化----");
    }

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

        //對request和response進行一些預處理
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        System.out.println("FilterDemo01執行前!!!");
        chain.doFilter(request, response);  //讓目标資源執行,放行
        System.out.println("FilterDemo01執行後!!!");
    }

    @Override
    public void destroy() {
        System.out.println("----過濾器銷毀----");
    }
}
           

4、Filter的生命周期

Filter的建立和銷毀都由WEB伺服器負責。web應用程式啟動,web伺服器建立Filter的執行個體對象,調用init方法,這個過程隻執行一次。

Filter的銷毀由web容器調用destroy方法銷毀Filter,釋放過濾器使用的資源。在Filter的生命周期内執行一次。

5、Decorator設計模式

5.1 Decorator模式介紹

在某個對象不能滿足業務需求時,通常有兩種方式來對其進行增強:

  • 編寫子類,繼承并重寫需要增強的方法。
  • 使用Decorator設計模式對方法進行增強。

一般情況下我們使用第一種方法,但在某個特殊情況下,我們隻能使用第二種方法,即:被增強的對象,隻能得到對象,不能得到class檔案。

在filter中可以得到request,response對象,可以使用decorator模式對request和response對象進行包裝, 在把包裝對象傳給目标資源。

5.2 Decorator模式實作

  1. 被增強對象內建了什麼接口和父類,編寫一個類,也繼承這些接口和父類。
  2. 在類中定義變量,變量類型即被增強對象的類型。
  3. 在類中定義構造函數,接收被增強對象。
  4. 覆寫需增強的方法,編寫增強的代碼。

Servlet API中提供了一個request對象的Decorator設計模式的預設實作類HttpServletRequestWrapper,(HttpServletRequestWrapper類實作了request接口中的所有方法,但這些方法的内部實作都是僅僅調用了一下所包裝的request對象的對應方法)以避免使用者在對request對象進行增強時需要實作request接口中的所有方法。

是以當需要增強request對象時,隻需要寫一個類繼承HttpServletRequestWrapper類,然後在重寫需要增強的方法即可

當需要增強response對象時,繼承HttpServletResponseWrapper類。

6、Filter的應用

6.1 統一全站字元編碼

public class EncodingFilter implements Filter {

    String encoding = null;
    FilterConfig filterConfig = null;

    public void init(FilterConfig config ) throws ServletException{
        this.filterConfig=config;
        this.encoding=filterConfig.getInitParameter("encoding");

    }

    public void doFilter(ServletRequest request , ServletResponse response , FilterChain chain)
    throws IOException , ServletException{
        if(encoding !=null){
            request.setCharacterEncoding(encoding);

        }
        chain.doFilter(request, response);
    }
    public void destroy(){
        this.encoding = null;
        this.filterConfig = null;
    }
}
           

在init方法中獲得XML注冊時的encoding屬性值,将它傳遞給該類的屬性encoding,在doFilter方法中,使用erquest.setCharacterEncoding()方法,設定request的屬性值。

6.2 禁止浏覽器緩存動态頁面

public class NoCacheFilter implements Filter{
    public void init (){}

    public void doFilter(ServletRequest req , ServletResponse resp, FliterChain chain) 
    throws IOException ,ServletException{
        //強制轉換
        HttpServletRequest request = (HttpServletRequest )req;
        HttpServletResponse response = (HttpServletResponse ) resp;
        //三種方法設定禁止緩存
        response.setDataHeader("Expires",-1);
        response.setHeader("Cache-control","no-cache");
        response.setHeader("Pragma","no-cache");

        chain.doFilter(request,response);
    }

    public void destroy(){}

}
           

XML配置:

<filter>
      <filter-name>NoCacheFilter</filter-name>
      <filter-class>web.filter.NoCacheFilter</filter-class>
</filter>

<filter-mapping>
      <filter-name>NoCacheFilter</filter-name>
        <!--隻攔截Jsp請求-->
      <servlet-name>*.jsp</servlet-name>
</filter-mapping>
           

HTTP響應頭字段有三種禁止浏覽器緩存的頭字段:

  • Expires:告訴浏覽器把回送的資源緩存多長時間 -1或0則是不緩存
  • Cache-Control:no-cache,控制浏覽器不要緩存資料
  • Pragma:no-cache,控制浏覽器不要緩存資料

HTTP請求中的常用請求字段和HTTP的響應狀态碼及響應頭

http://blog.csdn.net/qxs965266509/article/details/8082810

6.3 控制浏覽器緩存靜态頁面

public class NoCacheFilter implements Filter{
    public void init (){}

    public void doFilter(ServletRequest req , ServletResponse resp, FliterChain chain) 
    throws IOException ,ServletException{
        //強制轉換
        HttpServletRequest request = (HttpServletRequest )req;
        HttpServletResponse response = (HttpServletResponse ) resp;
        //得到請求資源的rul
        String url = request.getRequestURI();
        //得到請求資源的字尾
        String ext = uri.substring(uri.lastIndexOf(".")+1);
        //得到該字尾在xml設定中的緩存時間屬性值
        String time = filterConfig.getInitParameter(ext);

        if(time!=null){
            long t = Long.parseLong(time)*3600*1000;
            //設定響應頭字段Expires的緩存時間
            response.setDataHeader("Expires",System.currentTimeMillis()+t);
        }
    }

    public void destroy(){}

}
           

XML配置:

<!-- 配置緩存過濾器 -->
   <filter>
      <filter-name>CacheFilter</filter-name>
      <filter-class>me.gacl.web.filter.CacheFilter</filter-class>
       <!-- 配置要緩存的web資源以及緩存時間,以小時為機關 -->
      <init-param>
          <param-name>css</param-name>
          <param-value>4</param-value>
      </init-param>
      <init-param>
          <param-name>jpg</param-name>
          <param-value>1</param-value>
      </init-param>
      <init-param>
          <param-name>js</param-name>
          <param-value>4</param-value>
      </init-param>
      <init-param>
          <param-name>png</param-name>
          <param-value>4</param-value>
      </init-param>
  </filter>

  <!-- 配置要緩存的web資源的字尾-->
  <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>
   <filter-mapping>
      <filter-name>CacheFilter</filter-name>
      <url-pattern>*.png</url-pattern>
  </filter-mapping>
           

6.4 使用者自動登陸

引用redarmychen的部分代碼

實作使用者自動登陸的思路:

  • 使用者首次登陸成功後,發送一個名稱為user的cookie給用戶端,cookie的值為使用者名和加密的密碼,同時在session對象中存放一個使用者登入記錄的屬性值。
  • 編寫一個LoginFilter,filter檢查session的屬性值是否比對,或者檢查使用者是否帶有名稱為user的cookie,(如果有,則調用dao查詢cookie的使用者名和密碼是否和資料庫比對)以實作程式完成自動登陸。

LoginServlet的部分代碼

建立cookie:

long time = System.currentTimeMillis() + expires  * 1000;  
//cookie拼接的value值,(可以根據自己的想法設計)  
String cookieValue = name + ":" + time + ":"  + md5Value(name + ":" + pass + ":" + time);  
//建立cookie  
autoCookie = new Cookie("autologin", cookieValue); 
           

添加cookie:

autoCookie.setMaxAge((int) expires);  
autoCookie.setPath("/day56");  
// 添加cookie  
response.addCookie(autoCookie); 
           

添加session對象:

request.getSession().setAttribute("admin", entity);
           

LoginFilter的部分代碼

檢視session中是否有标志:

// 1.首先判斷sesion中有沒有admin  
Object object = request.getSession().getAttribute("admin");  
// 如果session中有使用者  
if (object != null) {  
    // 跳轉到成功登入的界面  
    request.getRequestDispatcher("./sc.jsp").forward(request, response);  
    return;  
} 
           

檢視cookie中是否有使用者登入記錄:

// 2.判斷cookie中是否存在 autologin辨別符 的cookie對象  
        // 聲明cookie  
        Cookie autoCookie = null;  
        // 擷取所有的cookie  
        Cookie cookies[] = request.getCookies();  
        // 如果沒有cookie資訊,就繼續執行login.do,跳轉到login.jsp頁面  
        if (cookies != null) {  
            // 如果有,就周遊cookie  
            for (Cookie cookie : cookies) {  
                // 判斷cookie中是否有autologin辨別符的cookie  
                if ("autologin".equals(cookie.getName())) {  
                    autoCookie = cookie; // 如果有 就指派給臨時變量autoCookie  
                }  
            }  

            // 3. 判斷autoCookie是否等于null  
            if (autoCookie == null) {  
                // 如果等于null,則繼續執行login.jsp頁面  
                chain.doFilter(request, response);  
                return;  
            }  

            // 3.如果autoCookie不等于null,就判斷cookie的值  
            // 擷取cookie值  
            String value = autoCookie.getValue();  
            // 拆分cookie的值  
            String temp[] = value.split(":");  
            System.out.println(temp.length);  
            // 判斷長度 是否等于自己拼接的長度  
            if (temp.length != 3) {  
                // 如果不等于3,則繼續執行login.jsp頁面  
                chain.doFilter(request, response);  
                return;  
            }  

            // 擷取cookie拆分的各個值  
            String name = temp[0]; // 使用者名  
            String time = temp[1];// 擷取有效時間  
            String service_md5Value = temp[2];// 擷取md5的加密後的字元  
            // 4.判斷cookie是否失效  
            if (Long.valueOf(time) <= System.currentTimeMillis()) {  
                // 如果失效,則繼續執行login.jsp頁面  
                chain.doFilter(request, response);  
                return;  
            }  

            // 5.如果cookie沒有失效,根據使用者名,去查詢使用者資訊  
            AdminService adminService = new AdminServiceImpl();  
            // 查詢使用者資訊  
            Admin entity = adminService.checkLogin(name);  
            System.out.println(entity+"0000");  
            // 判斷使用者是否為null  
            if (entity == null) {  
                // 如果沒有查詢的使用者,則繼續執行login.jsp頁面  
                chain.doFilter(request, response);  
                return;  
            }  

            // 按照伺服器拼接的字元的方式,拼接md5加密的字元串  
            String md5Temp = entity.getName() + ":" + entity.getPass() + ":"  
                    + time;  
            // 判斷md5加密後和伺服器端加密的字元是否相等  
            if (!(md5Value(md5Temp).equals(service_md5Value))) {  
                // 在不相等的情況下,則繼續執行login.jsp頁面  
                chain.doFilter(request, response);  
                return;  
            }  

            // 如果滿足了cookie取值判斷的所有結果,則跳轉到成功登入的界面.  
            request.getSession().setAttribute("admin", entity);  
            request.getRequestDispatcher("./sc.jsp").forward(request, response);  
        } else {  
            // 在沒有cookie資訊的時候,則繼續login.jsp頁面  
            chain.doFilter(request, response);  
            return;  
        }  
    }