Servlet過濾器

1、什麼是Servlet過濾器java

過濾器是在數據交互之間過濾數據的中間組件,獨立於任何平臺或者 Servlet 容器。web

Servlet過濾器能夠應用在客戶機和 servlet 之間、servlet 和 servlet 或 JSP 頁面之間,以及所包括的每一個 JSP 頁面之間。
2、實現一個 Servlet 過濾器apache

 

 Servlet過濾器 API 包含javax.servlet包中的 3 個接口,分別是 Filter 、 FilterChain 和 FilterConfig 。
 實現一個 Servlet 過濾器的確要經歷三個步驟。瀏覽器


  一、編寫 Servlet 過濾器實現類。
  二、配置 Servlet 過濾器。把該過濾器添加到 Web 應用程序中(經過在 Web 部署描述符 /web.xml 中聲明它);
  三、部署 Servlet 過濾器。把過濾器與應用程序一塊兒打包並部署它;緩存


 Servlet容器對部署描述符中聲明的每個過濾器,只建立一個實例(或實例池)。
 與Servlet相似,容器將在同一個過濾器實例上運行多個線程來同時爲多個請求服務,所以,開發過濾器時,也要注意線程安全的問題。安全

 

3、編寫過濾器實現類服務器

 一、Filter接口:全部的Servlet過濾器類都必須實現javax.servlet.Filter接口
  a、init(FilterConfig):
   這是Servlet過濾器的初始化方法,Servlet容器建立Servlet過濾器實例後將調用這個方法。在這個方法中能夠讀取web.xml文件中Servlet過濾器的初始化參數。session

  b、doFilter(ServletRequest,ServletResponse,FilterChain):
   這個方法完成實際的過濾操做,當客戶請求訪問於過濾器關聯的URL時,Servlet容器將先調用過濾器的doFilter方法。FilterChain參數用於訪問後續過濾器。oracle

  c、destroy():
   Servlet容器在銷燬過濾器實例前調用該方法,這個方法中能夠釋放Servlet過濾器佔用的資源。app

 二、過濾器實現類建立步驟(過濾器實現類生命週期):
  a.實現javax.servlet.Filter接口。
  b.初始化:實現init方法,讀取過濾器的初始化參數。
  c.過濾:實現doFilter方法,完成對請求或響應的過濾。
  d.轉發或阻塞:調用FilterChain接口對象的doFilter方法,向後續的過濾器傳遞請求或響應。
  e.析構:destroy方法銷燬過濾器,釋放過濾器佔用的資源。

4、配置 Servlet 過濾器(在web.xml中配置)

 

 過濾器經過 web.xml 文件中的兩個 XML 標籤來聲明:
  一、<filter> : 定義過濾器的名稱,而且聲明過濾器實現類和 init() 參數。
   <filter-name> : 指定過濾器的名字;
   <filter-class> : 指定過濾器類的類名,包括類的路徑;
   <init-param> : 爲過濾器實例提供初始化參數,能夠有多個;

  二、<filter-mapping> : 將過濾器與 servlet 或 URL 模式相關聯。
   <filter-name> :  指定過濾器的名字,與<filter>中的子元素<filter-name>相對應;
   <url-pattern> :  指定和過濾器關聯的URL,爲」/*」表示全部URL;

  三、<filter-mapping>元素還能夠包含0到4個<dispatcher>,指定過濾器對應的請求方式,
    能夠是REQUEST,INCLUDE,FORWARD和ERROR之一,默認REQUEST.

 REQUEST
     當用戶直接訪問頁面時,Web容器將會調用過濾器。若是目標資源是經過RequestDispatcher的include()或forward()方法訪問時,那麼該過濾器就不會被調用。
   INCLUDE
   若是目標資源是經過RequestDispatcher的include()方法訪問時,那麼該過濾器將被調用。除此以外,該過濾器不會被調用。
   FORWARD
   若是目標資源是經過RequestDispatcher的forward()方法訪問時,那麼該過濾器將被調用,除此以外,該過濾器不會被調用。
   ERROR
     若是目標資源是經過聲明式異常處理機制調用時,那麼該過濾器將被調用。除此以外,過濾器不會被調用。

 在web.xml中配置Servlet和Servlet過濾器,應該先聲明過濾器元素,再聲明Servlet元素。
 兩個或更多個過濾器應用到同一個資源,按照它們在配置文件中顯示的前後次序調用它們。

 例子1:單個過濾器配置:容器將其應用於全部接收的請求
 <filter>   
  <filter-name>FilterName</filter-name>   
  <filter-class></filter-class> 
 </filter> 
 <filter-mapping>   
  <filter-name>FilterName</filter-name>   
  <url-pattern>/*</url-pattern> 
 </filter-mapping>

  例子2:過濾器應用到特定目錄或資源(文件)的配置:此容器只有在接收到對 /mydocs 目錄中的資源的請求時纔會應用該過濾器。
   <filter>   
  <filter-name>FilterName</filter-name>   
  <filter-class>packageName.FilterName</filter-class> 
 </filter> 
 <filter-mapping>   
  <filter-name>FilterName</filter-name>   
  <url-pattern>/mydocs/*</url-pattern> 
 </filter-mapping>

 例子3:定義一個過濾器鏈:兩個或更多個過濾器應用到同一個資源,按照它們在配置文件中顯示的前後次序調用它們。
   <filter>   
  <filter-name>FilterOne</filter-name>   
  <filter-class>packageName.FilterOne</filter-class> 
 </filter> 
 <filter-mapping>   
  <filter-name>FilterOne</filter-name>   
  <url-pattern>/*</url-pattern> 
 </filter-mapping>

   <filter>   
  <filter-name>FilterTwo</filter-name>   
  <filter-class>packageName.FilterTwo</filter-class> 
 </filter> 
 <filter-mapping>   
  <filter-name>FilterTwo</filter-name>   
  <url-pattern>/mydocs/*</url-pattern> 
 </filter-mapping>
 

5、部署 Servlet 過濾器


 只需把過濾器類和其餘 Web 組件類包括在一塊兒,把 web.xml 文件(連同過濾器定義和過濾器映射聲明)放進 Web 應用程序結構中,servlet 容器將處理以後的其餘全部事情。


6、Servlet 過濾器實現注意事項


 1.因爲Filter、FilterConfig、FilterChain都是位於javax.servlet包下,並不是HTTP包所特有的,
 因此ServletRequest、ServletResponse在使用前都必須先轉換成HttpServletRequest、HttpServletResponse再進行下一步操做。

 2.在web.xml中配置Servlet和Servlet過濾器,應該先聲明過濾器元素,再聲明Servlet元素。

 3.若是要在Servlet中觀察過濾器生成的日誌,應該確保在server.xml的localhost對應的<host>元素中配置以下<logger>元素:
  <Logger className = 「org.apache.catalina.logger.FileLogger」
   directory = 「logs」prefix = 「localhost_log.」suffix=」.txt」
   timestamp = 「true」/>

7、過濾器的實現方式


 過濾器實現方式在不保證功能前提下,從性能角度考慮有以下前後順序:Decorator或Proxy模式;AOP攔截器。

 1,標準Servlet控制器
  包裝了請求和響應對象。
  web容器來充當過濾管理器(FilterManager)和過濾鏈(FilterChain)來管理和協調過濾器。
 
  問題:功能覆蓋範圍上,也是一個scope,若是功能需求要爲某個類的方法實現實現過濾,使用一個Servlet Filter這樣過濾器實現。
   可是它對全部的Servlet請求都進行過濾,這無疑範了殺雞取卵的錯誤,會形成系統性能上的損失。

 2,使用Decorator模式來定製過濾器
  若是過濾器是業務邏輯的一部分,並且在設計時,咱們能夠肯定這些過濾器,進行特定指定的攔截。
  Decorator模式在點上針對性至關強,特別在這個點上有一系列過濾器須要實現時。
 
  若是某個過濾功能是不少類都須要的,會造成不少Decorator附加類,形成點造成面的狀況,則升級使用AOP攔截器。

  優勢:可以動態地爲過濾器擴展功能。
   也可使用FilterManager和FilterChain過濾器鏈負責協調和管理過濾處理,這樣單獨的過濾器就不用和其餘過濾器直接通訊了。

  問題:不能以一種標準的可移植的方式支持對請求和響應對象的包裝,並且不可以修改請求對象。
   缺少完善的緩存機制。當過濾器要控制輸出流的時候,還必須引入某種形式的緩存機制。

 3,使用模板方法模式來定製過濾器。
  能夠和其餘方法混用。
  優勢:這種方式是基於標準的過濾器的,基本過濾器做爲一個基類,封裝了過濾器API的全部細節。
   專一於預處理和後處理的全部邏輯。
   基本過濾器聲明瞭每一個過濾器要完成的方法,每一個過濾器子類來定義這些定義方法。由超類來控制子類的控制流程。
 
 4,使用AOP框架攔截器。
  當功能不是針對某個具體類或方法(方法權限除外),而是一系列類,使用動態AOP攔截器,性能損耗也是值得的,並且是必要的。
 
8、過濾器的應用場合

簡潔的說法:
 1.認證過濾:對用戶請求進行統一認證。
 
 2.登陸和審覈過濾:對用戶的訪問請求進行審覈和對請求信息進行日誌記錄。

 3,數據過濾:對用戶發送的數據進行過濾,修改或替換。

 4.圖像轉換過濾 :轉換圖像的格式。

 5.數據壓縮過濾 :對請求內容進行解壓,對響應內容進行壓縮。

 6.加密過濾 :對請求和響應進行加密處理。

 7.令牌過濾 :身份驗證

 8.資源訪問觸發事件過濾 :

 9.XSL/T過濾

 10.Mime-type過濾


複雜的說法:
在適合使用裝飾過濾器模式或者攔截器模式的任何地方,均可以使用過濾器:


加載:對於到達系統的全部請求,過濾器收集諸如瀏覽器類型、一天中的時間、轉發 URL 等相關信息,並對它們進行日誌記錄。

性能:過濾器在內容經過線路傳來並在到達 servlet 和 JSP 頁面以前解壓縮該內容,而後再取得響應內容,並在將響應內容發送到客戶機機器以前將它轉換爲壓縮格式。

安全:過濾器處理身份驗證令牌的管理,並適當地限制安全資源的訪問,提示用戶進行身份驗證和/或將他們指引到第三方進行身份驗證。
 過濾器甚至可以管理訪問控制列表(Access Control List,ACL),以便除了身份驗證以外還提供受權機制。
 將安全邏輯放在過濾器中,而不是放在 servlet 或者 JSP 頁面中,這樣提供了巨大的靈活性。
 在開發期間,過濾器能夠關閉(在 web.xml 文件中註釋掉)。
 在生產應用中,過濾器又能夠再次啓用。此外還能夠添加多個過濾器,以便根據須要提升安全、加密和不可拒絕的服務的等級。

會話處理:將 servlet 和 JSP 頁面與會話處理代碼混雜在一塊兒可能會帶來至關大的麻煩。
 使用過濾器來管理會話可讓 Web 頁面集中精力考慮內容顯示和委託處理,而沒必要擔憂會話管理的細節。

XSLT 轉換:無論是使用移動客戶端仍是使用基於 XML 的 Web 服務,無需把邏輯嵌入應用程序就在 XML 語法之間執行轉換的能力都絕對是無價的。


9、MVC 體系結構中的Servlet過濾器

 

 無論過濾器處於什麼位置,過濾器在處理流中的應用都是相同的。過濾器旨在擴充 MVC 體系結構的請求/響應處理流。
 從 MVC 的觀點看,調度器組件(它或者包括在控制器組件中,或者配合控制器組件工做)把請求轉發給適當的應用程序組件以進行處理。
 這使得控制器層成爲包括 Servlet 過濾器的最佳位置。經過把過濾器放在控制器組件自己的前面,過濾器能夠應用於全部請求,
 或者經過將它放在控制器/調度器與模型和控制器之間,它能夠應用於單獨的 Web 組件。

 

10、應用示例或說明


 1,使用過濾器認證用戶:
 每一個過濾器也能夠配置初始化參數,能夠將不須要過濾的地址配置到這個Filter的配置參數中,
 過濾時,若是請求地址在配置參數中,則放行,這樣就避免了在程序中硬編碼。
 每一個Filter中初始化時,均可以獲得配置對象,在Filter中配置二個不須要過濾的地址,一個是登錄頁面,一個是執行登錄認證的servlet;

 2,登陸和審覈過濾的示例:使用 servlet 過濾器來控制終端用戶對應用程序特性的訪問:
 經過顯示基於用戶角色的用戶界面來控制對應用程序特性的訪問。企業用戶可以訪問特定頁面,但我的用戶不能訪問這樣的頁面。
 應用一個過濾器來處理用戶請求並返回合適的頁面。經過使用過濾器,您可以向一個基於 JSP 的應用程序添加這種類型的訪問控制而無需更改現有的代碼。
 
 這種基於過濾器的訪問控制模型也很靈活,由於把用戶角色映射到特定 JSP 的數據存儲在一個 XML 文件中。
 所以,您能夠修改映射而不用修改應用程序 — 不須要從新編譯或從新部署。

實施:
     每當用戶從一個包含 /controllerservlet模式的 URI 請求資源時就會調用AccessControlFilter的實例
 當訪問控制過濾器被初始化後,從映射文件讀取數據,而 AccessControlFilter.doFilter 方法負責處理過濾事務。
 在獲取了用戶角色和所請求頁面的 URL 以後,doFilter 把這些值與映射數據對比。
 若是所請求的頁面對用戶角色是合適的,那麼代碼將調用 chain.doFilter 方法來調用該頁面並繼續正常的處理。
 不然,代碼將在 chain.doFilter 以前調用 request.setAttribute,結果是 Controller Servlet 使用戶從新進入到登陸頁面。
 
相關文件:
一、web.xml —— Web 部署描述符文件
 <filter>   
  <filter-name>AccessControlFilter</filter-name>   
  <filter-class>oracle.otnsamples.ibfbs.control.AccessControlFilter</filter-class>     
 </filter>   
 <filter-mapping>    
  <filter-name>AccessControlFilter</filter-name>    
  <url-pattern>/controllerservlet</url-pattern> 
 </filter-mapping>
   
二、Control.xml —— 描述事件、用戶角色和 JSP 聯繫的xml文件

  <Event>
     <Name>BUYSTOCK</Name>
     <Class>oracle.otnsamples.ibfbs.trademanagement.helper.TradeManagementHelper</Class>
     <Method>buyStock</Method>
     <Screen>jsps/BuyStock.jsp</Screen>

     <Roles>
        <Role>USER</Role>
     </Roles>
  </Event>
   ...
  <Event>
     <Name>CORPUPLOAD</Name>
     <Class></Class>
     <Method></Method>
     <Screen>jsps/CorporateUpload.jsp</Screen>
    
  <Roles>
       <Role>CORP</Role>
     </Roles>
  </Event>
   ...
  <Event>
     <Name>CONFIGNEWSUPLOAD</Name>
     <Class>oracle.otnsamples.ibfbs.admin.helper.AdminHelper</Class>
     <Method>configNewsUpload</Method>

     <Screen>jsps/UploadData.jsp</Screen>
     <Roles>
        <Role>ADMIN</Role>
     </Roles>
  </Event>
   ...
  <Event>
     <Name>LOGIN</Name>
     <Class>oracle.otnsamples.ibfbs.usermanagement.helper.UserManagementHelper</Class>
     <Method>checkPassword</Method>
     <Screen>jsps/MyHome.jsp</Screen>

     <Roles>
        <Role>DEFAULT</Role>
        <Role>USER</Role>
        <Role>CORP</Role>
        <Role>ADMIN</Role>
     </Roles>
  </Event>    


 
三、AccessControlFilter.java —— 過濾器類

 

 //讀取Control.xml,對用戶的權限作檢查

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)                      
  throws IOException, ServletException {
  
  HttpSession session = ((HttpServletRequest) request).getSession();   
  String eventName = request.getParameter("EVENTNAME");   
  if (eventName != null && urlMap != null ) {     
   String role = (String) session.getAttribute("ROLE");     
   if (role == null) {
    role = "DEFAULT";     
   }

   URLMapping event = (URLMapping) urlMap.get(eventName);     
  
   if ((event != null) && (event.getRoles() != null) && (event.getRoles().length > 0)) {       
    // New session so not logged in yet. Redirect to login page       
    if (session.isNew())  {       
     request.setAttribute("EVENTNAME", "FIRSTPAGE");
    }      
    // If invalid access, redirect to login page       
    else  if (!event.isValidRole(role))  {       
     request.setAttribute("EVENTNAME", "LOGINPAGE");
    }    
   }
   
  }else {     
   request.setAttribute("EVENTNAME", "FIRSTPAGE");   
  }   
  // The privileges are sufficient to invoke this URL, continue normal   
  // processing of the request   
  chain.doFilter(request, response); 
 }
 

 四、對請求信息進行日誌記錄的示例

 

 public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)throws ServletException, IOException {
       //把ServletRequest對象構造爲HttpServletRequest

   //從請求中提出須要的進行日誌記錄的信息
       String url = req.getRequestURI();

       HttpSession so = req.getSession();
       String canLog = (String)so.getAttribute(url);

       //若是第一次訪問該頁面,就進行日誌處理
       if (canLog == null) {
        so.setAttribute(url, "Y");
        doLog();
       }
       chain.doFilter(request,response);
  }
 

五、用過濾器來解決客戶端和服務器端編碼一致,防止中文亂碼的問題。


  public class CharacterEncodingFilter implements Filter {
    protected FilterConfig filterConfig = null;
    protected String encoding = "";

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
     throws IOException, ServletException {

           if(encoding != null) {
             servletRequest.setCharacterEncoding(encoding);
    }
      filterChain.doFilter(servletRequest, servletResponse);
    }

    public void destroy() {
      filterConfig = null;
      encoding = null;
   }

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

    }
  }
 


 六、使 Browser瀏覽器 不緩存頁面的過濾器


  public class ForceNoCacheFilter implements Filter {

   public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
    throws IOException, ServletException {
 
    ((HttpServletResponse) response).setHeader("Cache-Control","no-cache");
      ((HttpServletResponse) response).setHeader("Pragma","no-cache");
      ((HttpServletResponse) response).setDateHeader ("Expires", -1);

      filterChain.doFilter(request, response);
    }

  }
 

七、用於檢測用戶是否登錄的過濾器,若是未登陸,則重定向到指的登陸頁面

  /**
   * 用於檢測用戶是否登錄的過濾器,若是未登陸,則重定向到指的登陸頁面<p>
   * 配置參數<p>
   * checkSessionKey 需檢查的在 Session 中保存的關鍵字<br/>
   * redirectURL 若是用戶未登陸,則重定向到指定的頁面,URL不包括 ContextPath<br/>
   * notCheckURLList 不作檢查的URL列表,以分號分開,而且 URL 中不包括 ContextPath<br/>
   */
  public class CheckLoginFilter implements Filter {
    protected FilterConfig filterConfig = null;
       private String redirectURL = null;
    private List notCheckURLList = new ArrayList();
    private String sessionKey = null;

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
    throws IOException, ServletException {

      HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;

           HttpSession session = request.getSession();
     
    if(sessionKey == null) {
        filterChain.doFilter(request, response);
        return;
      }
      if((!checkRequestURIIntNotFilterList(request)) &&
      session.getAttribute(sessionKey) == null) {
        response.sendRedirect(request.getContextPath() + redirectURL);
        return;
      }
      filterChain.doFilter(servletRequest, servletResponse);
    }

    public void destroy() {
      notCheckURLList.clear();
    }

    private boolean checkRequestURIIntNotFilterList(HttpServletRequest request) {

      String uri = request.getServletPath() + (request.getPathInfo() == null ? "" : request.getPathInfo());
      return notCheckURLList.contains(uri);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
      this.filterConfig = filterConfig;
      redirectURL = filterConfig.getInitParameter("redirectURL");
           sessionKey = filterConfig.getInitParameter("checkSessionKey");

      String notCheckURLListStr = filterConfig.getInitParameter("notCheckURLList");

      if(notCheckURLListStr != null) {         StringTokenizer st = new StringTokenizer(notCheckURLListStr, ";");         notCheckURLList.clear();         while(st.hasMoreTokens()) {           notCheckURLList.add(st.nextToken());         }       }     }   }

相關文章
相關標籤/搜索