本文主要介紹Servlet過濾器的基本原理

author: ZJ 2007-2-21
1Servlet過濾器

1.1 什麼是過濾器

過濾器是一個程式,它先於與之相關的servletJSP頁面執行在伺服器上。過濾器可附加到一個或多個servletJSP頁面上,並且可以檢查進入這些資源的請求資訊。在這之後,過濾器可以作如下的選擇:
①以常規的方式呼叫資源(即,呼叫servletJSP頁面)。

②利用修改過的請求資訊呼叫資源。

③呼叫資源,但在傳送響應到客戶機前對其進行修改。

④阻止該資源呼叫,代之以轉到其他的資源,返回一個特定的狀態程式碼或生成替換輸出。

 

1.2 Servlet過濾器的基本原理

Servlet作為過濾器使用時,它可以對客戶的請求進行處理。處理完成後,它會交給下一個過濾器處理,這樣,客戶的請求在過濾鏈裡逐個處理,直到請求傳送到目標為止。例如,某網站裡有提交“修改的註冊資訊”的網頁,當使用者填寫完修改資訊並提交後,伺服器在進行處理時需要做兩項工作:判斷客戶端的會話是否有效;對提交的資料進行統一編碼。這兩項工作可以在由兩個過濾器組成的過濾鏈裡進行處理。當過濾器處理成功後,把提交的資料傳送到最終目標;如果過濾器處理不成功,將把檢視派發到指定的錯誤頁面。
2Servlet過濾器開發步驟

開發Servlet過濾器的步驟如下:
①編寫實現Filter介面的Servlet類。
②在web.xml中配置Filter
開發一個過濾器需要實現Filter介面,Filter介面定義了以下方法:
destory()由Web容器呼叫,初始化此Filter

initFilterConfig
filterConfig
)由Web容器呼叫,初始化此Filter

doFilterServletRequest
request,ServletResponse response,FilterChain chain
)具體過濾處理程式碼。

3.一個過濾器框架例項

SimpleFilter1.java

package com.zj.sample;

import java.io.IOException;

import javax.servlet.Filter;

import
javax.servlet.FilterChain;

import
javax.servlet.FilterConfig;

import
javax.servlet.ServletException;

import
javax.servlet.ServletRequest;

import
javax.servlet.ServletResponse;

 

public class SimpleFilter1 implements Filter {

    @SuppressWarnings(“unused”)

    private FilterConfig filterConfig;

 

    public void init(FilterConfig config) throws
ServletException {

       this.filterConfig = config;

    }

 

    public void doFilter(ServletRequest
request, ServletResponse response,

           FilterChain chain) {

       try {

           System.out.println(“Within SimpleFilter1:Filtering the Request…”);

           chain.doFilter(request, response);// 把處理髮送到下一個過濾器

           System.out .println(“Within SimpleFilter1:Filtering the
Response…”
);

       } catch (IOException ioe) {

           ioe.printStackTrace();

       } catch (ServletException se) {

           se.printStackTrace();

       }

    }

 

    public void destroy() {

       this.filterConfig = null;

    }

}

 

SimpleFilter2.java

package com.zj.sample;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import
javax.servlet.FilterConfig;

import
javax.servlet.ServletException;

import
javax.servlet.ServletRequest;

import
javax.servlet.ServletResponse;

 

public class SimpleFilter2 implements Filter {

    @SuppressWarnings(“unused”)

    private FilterConfig filterConfig;

 

    public void init(FilterConfig config) throws
ServletException {

       this.filterConfig = config;

    }

 

    public void doFilter(ServletRequest
request, ServletResponse response,

           FilterChain chain) {

       try {

           System.out.println(“Within SimpleFilter2:Filtering the Request…”);

           chain.doFilter(request, response); // 把處理髮送到下一個過濾器

           System.out.println(“Within SimpleFilter2:Filtering the Response…”);

       } catch (IOException ioe) {

           ioe.printStackTrace();

       } catch (ServletException se) {

           se.printStackTrace();

       }

    }

 

    public void destroy() {

       this.filterConfig = null;

    }

}

 

web.xml

<filter>

    <filter-name>filter1</filter-name>

    <filter-class>com.zj.sample.SimpleFilter1</filter-class>

</filter>

<filter-mapping>

    <filter-name>filter1</filter-name>

    <url-pattern>/*</url-pattern>//為所有的訪問做過濾

</filter-mapping>

 

<filter>

    <filter-name>filter2</filter-name>

    <filter-class>com.zj.sample.SimpleFilter2</filter-class>

</filter>

<filter-mapping>

    <filter-name>filter2</filter-name>

    <url-pattern>/*</url-pattern>//為所有的訪問做過濾

</filter-mapping>

 

開啟web容器中任意頁面輸出結果:(注意過濾器執行的請求/響應順序)

Within SimpleFilter1:Filtering the Request…
Within SimpleFilter2:Filtering the Request…
Within SimpleFilter2:Filtering the Response…
Within SimpleFilter1:Filtering the Response…

4.報告過濾器

我們來試驗一個簡單的過濾器,只要呼叫相關的servletJSP頁面,它就列印一條訊息到標準輸出。為實現此功能,在doFilter方法中執行過濾行為。每當呼叫與這個過濾器相關的servletJSP頁面時,doFilter方法就生成一個列印輸出,此輸出列出請求主機和呼叫的URL。因為getRequestURL方法位於HttpServletRequest而不是ServletRequest中,所以把ServletRequest物件構造為HttpServletRequest型別。我們改動一下章節3SimpleFilter1.java
SimpleFilter1.java

package com.zj.sample;

import java.io.IOException;

import java.util.Date;

import javax.servlet.Filter;

import
javax.servlet.FilterChain;

import
javax.servlet.FilterConfig;

import
javax.servlet.ServletException;

import
javax.servlet.ServletRequest;

import
javax.servlet.ServletResponse;

import
javax.servlet.http.HttpServletRequest;

 

public class SimpleFilter1 implements Filter {

    @SuppressWarnings(“unused”)

    private FilterConfig filterConfig;

 

    public void init(FilterConfig config) throws
ServletException {

       this.filterConfig = config;

    }

 

    public void doFilter(ServletRequest
request, ServletResponse response,

           FilterChain chain) {

       try {

           System.out.println(“Within SimpleFilter1:Filtering the Request…”);

           HttpServletRequest req =
(HttpServletRequest) request;

           System.out.println(req.getRemoteHost() + ” tried to access “

                  + req.getRequestURL() + ” on “ + new Date() + “.”);

           chain.doFilter(request, response);

           System.out.println(“Within SimpleFilter1:Filtering the Response…”);

       } catch (IOException ioe) {

           ioe.printStackTrace();

       } catch (ServletException se) {

           se.printStackTrace();

       }

    }

 

    public void destroy() {

       this.filterConfig = null;

    }

}

 

web.xml設定不變,同章節3

 

測試:

輸入[url]http://localhost:8080/Test4Jsp/login.jsp[/url]

 

結果:

Within SimpleFilter1:Filtering the Request…
0:0:0:0:0:0:0:1 tried to access [url]http://localhost:8080/Test4Jsp/login.jsp[/url] on Sun Mar 04 17:01:37 CST 2007.
Within SimpleFilter2:Filtering the Request…
Within SimpleFilter2:Filtering the Response…
Within SimpleFilter1:Filtering the Response…

5.訪問時的過濾器(在過濾器中使用servlet初始化引數)
下面利用init設定一個正常訪問時間範圍,對那些不在此時間段的訪問作出記錄。我們改動一下章節3SimpleFilter2.java

SimpleFilter2.java

package com.zj.sample;

import java.io.IOException;

import java.text.DateFormat;

import java.util.Calendar;

import
java.util.GregorianCalendar;

import javax.servlet.Filter;

import
javax.servlet.FilterChain;

import
javax.servlet.FilterConfig;

import
javax.servlet.ServletContext;

import
javax.servlet.ServletException;

import
javax.servlet.ServletRequest;

import
javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

 

public class SimpleFilter2 implements Filter {

    @SuppressWarnings(“unused”)

    private FilterConfig config;

    private ServletContext context;

    private int startTime, endTime;

    private DateFormat formatter;

 

    public void init(FilterConfig config) throws ServletException {

       this.config = config;

       context = config.getServletContext();

       formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,

              DateFormat.MEDIUM);

       try {

           startTime = Integer.parseInt(config.getInitParameter(“startTime”));// web.xml

           endTime = Integer.parseInt(config.getInitParameter(“endTime”));// web.xml

       } catch (NumberFormatException nfe)
{
// Malformed or null

           // Default: access at or after 10 p.m. but before 6 a.m. is

           // considered unusual.

           startTime = 22; // 10:00 p.m.

           endTime = 6; // 6:00 a.m.

       }

    }

 

    public void doFilter(ServletRequest
request, ServletResponse response,

           FilterChain chain) {

       try {

           System.out.println(“Within SimpleFilter2:Filtering the Request…”);

           HttpServletRequest req =
(HttpServletRequest) request;

           GregorianCalendar calendar = new
GregorianCalendar();

           int currentTime = calendar.get(Calendar.HOUR_OF_DAY);

           if (isUnusualTime(currentTime, startTime, endTime)) {

              context.log(“WARNING: “ + req.getRemoteHost()
+
” accessed “

                     + req.getRequestURL() + ” on “

                     + formatter.format(calendar.getTime()));

              // The log file is under <CATALINA_HOME>/logs.One log per day.

           }

           chain.doFilter(request, response);

           System.out

                  .println(“Within SimpleFilter2:Filtering the
Response…”
);

       } catch (IOException ioe) {

           ioe.printStackTrace();

       } catch (ServletException se) {

           se.printStackTrace();

       }

    }

 

    public void destroy() {}

 

    // Is
the current time between the start and end

    // times
that are marked as abnormal access times?

    private boolean isUnusualTime(int currentTime, int startTime, int endTime) {

       // If the start time is less than the end time (i.e.,

       // they are two times on the same day), then the

       // current time is considered unusual if it is

       // between the start and end times.

       if (startTime < endTime) {

           return ((currentTime >=
startTime) && (currentTime < endTime));

       }

       // If the start time is greater than or equal to the

       // end time (i.e., the start time is on one day and

       // the end time is on the next day), then the current

       // time is considered unusual if it is NOT between

       // the end and start times.

       else {

           return (!isUnusualTime(currentTime,
endTime, startTime));

       }

    }

}

 

web.xml設定不變。

關於Tomcat日誌處理,這裡補充介紹一下。config.getServletContext().log“log message”)會將日誌資訊寫入<CATALINA_HOME>/logs資料夾下,檔名應該為localhost_log.2007-03-04.txt這樣的形式(按日期每天產生一個,第二天可以看見)。要得到這樣一個日誌檔案,應該在server.xml中有:
<Logger
className=”org.apache.catalina.logger.FileLogger”
prefix=”catalina_log.” suffix=”.txt”
timestamp=”true”/>
參考資料

[1] Marty Halls ServletJSP權威指南,機械工業出版社
[2] 趙強,精通JSP程式設計,電子工業出版社