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()); } } } }