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>
-
标簽用于設定filter的初始化參數,<init-param>
和<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>
标簽設定使用者以什麼方式通路時,過濾器工作。标簽的屬性值:
- REQUEST:使用者直接通路頁面,過濾器工作。
- INCLUDE:目标資源通過RequestDispatcher的include()方法通路,過濾器工作。
- FORWARD:目标資源通過RequestDispatcher的forward()方法通路,過濾器工作。
- 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模式實作
- 被增強對象內建了什麼接口和父類,編寫一個類,也繼承這些接口和父類。
- 在類中定義變量,變量類型即被增強對象的類型。
- 在類中定義構造函數,接收被增強對象。
- 覆寫需增強的方法,編寫增強的代碼。
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;
}
}