過濾器是一個駐留在服務器端的Web組建,能夠截取客戶端和資源之間的請求和響應信息。Web過濾器是不能直接處理客戶端請求,返回客戶端數據的!css
舉例來講:當咱們登陸CSDN或郵箱的時候,輸入應用名和密碼就能夠進入咱們請求的頁面,當咱們點擊退出後,下一次進入時須要從新輸入登陸用戶名與密碼。這是過濾器應用的一個場景。html
咱們須要瞭解:java
一、過濾器的工做原理web
上面的圖簡單說明了過濾器在客戶端和服務器之間的做用。緩存
二、過濾器的生命週期服務器
Web容器啓動的時候,會加載web.xml並執行一次init()函數,而後每次客戶端的請求都會執行doFilter()函數,最後當容器關閉的時候會執行destroy()函數cookie
上面顯示的是一個Web應用程序的結構,全部的Webroot中的內容都是Web的內容,Web-INF下全部的資源都不能直接被url訪問,其餘的文件,用戶能夠經過url訪問。session
關於url-pattern的書寫規範,app
(A)一個filter映射一個url:這種狀況下的url與url-pattern中配置的url進行精確匹配。url-pattern中的訪問路徑必須以 / 開頭,表示的是Web應用程序的根目錄,而不是Web站點的根目錄,路徑名稱能夠是多級目錄的形式,例如jsp
<url-pattern>/demo/index.html</url-pattern>
(B)一個filter映射多個url:這種狀況下可使用通配符,須要注意的也有兩種狀況:
(1)*.擴展名,*點前面不能有 「/」
(2)以/開頭,並以 /* 結尾,例如
<url-pattern>/action/*</url-pattern>表示的是整個action目錄下的url
<url-pattern>/</url-pattern>表示的是整個web應用程序下的url
過濾器鏈
每次客戶端的請求,服務器都會Filter中的doFilter方法,所以,咱們能夠在diFilter中完成一些處理,下面是一個簡單的代碼實現:
public class FilterFirst implements Filter { // 應用被加載時完成過濾器的實例化 public FilterFirst() { System.out.println("調用了默認構造方法"); } // 初始化:服務器傳入FilterConfig參數 public void init(FilterConfig filterConfig) throws ServletException { System.out.println("調用初始化方法"); } // 用戶每次訪問被過濾資源都會調用過濾器的doFilter方法 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("FilterDemo1第一次攔截"); // 對request、response進行預處理,代碼放在chain.doFilter()前 chain.doFilter(request, response);// 放行 // 對響應信息進行攔截,代碼放在chain.doFilter()後 System.out.println("FilterDemo1第二次攔截"); } // 應用從服務器上卸載時調用銷燬方法 public void destroy() { System.out.println("調用銷燬方法"); } }
web.xml的配置
<filter> <filter-name>FilterDemo1</filter-name> <filter-class>org.flyne.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
注意代碼中出現的doFilter函數以及其中的參數。ServletRequest request, ServletResponse response, FilterChain chain 所以:
(1)咱們能夠實現將客戶端到服務器,服務器到客戶端的交換數據進行處理。
(2) 處理完成後,咱們必定要記得寫上chain.doFilter(request, response);不然,Web訪問會一直處於被攔截的狀態;
4、實例
1)SetCharacterEncodingFilter:解決POST請求參數和響應輸出的中文亂碼
public class SetCharacterEncodingFilter implements Filter { private String encoding; //編碼能夠經過<init-param>元素配置 public void init(FilterConfig filterConfig) throws ServletException { encoding = filterConfig.getInitParameter("encoding"); if(encoding == null){//若是用戶忘記配置,默認encoding爲UTF-8 encoding = "UTF-8"; } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //只能解決POST請求參數亂碼問題 request.setCharacterEncoding(encoding); //指定輸出編碼(最後帶上,後面會有說明) response.setCharacterEncoding(encoding); //指定輸出流編碼及客戶端應使用的碼錶 response.setContentType("text/html;charset="+encoding); chain.doFilter(request, response); } public void destroy() { } } ------------------------web.xml配置------------------------ <filter> <filter-name>SetCharacterEncodingFilter</filter-name> <filter-class>org.flyne.examples.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>SetCharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2)2)NoCacheFilter:禁止客戶端緩存動態資源
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { //響應頭爲HTTP協議裏的,需轉換一下 HttpServletRequest request = null; HttpServletResponse response = null; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("not-http request or response"); } response.setHeader("Expires", "0"); response.setHeader("Cache-Control", "no-cache"); // response.setHeader("Pragma", "no-cache"); //這三個參數的意義差很少 chain.doFilter(request, response); } ------------------------web.xml配置------------------------ <filter> <filter-name>NoCacheFilter</filter-name> <filter-class>org.flyne.examples.NoCacheFilter</filter-class> </filter> <filter-mapping> <filter-name>NoCacheFilter</filter-name> <url-pattern>/servlet/*</url-pattern> <url-pattern>*.jsp</url-pattern> </filter-mapping>
3)SetCacheExpiresFilter:控制靜態資源緩存時間 (這個主要是理解HTTP協議中Expire的做用)
public class SetCacheExpiresFilter implements Filter { private int htmlExp;//HTML的緩存時間,單位爲小時,下同 private int cssExp;//CSS的緩存時間 private int jsExp;//JS的緩存時間 public void init(FilterConfig filterConfig) throws ServletException { htmlExp = Integer.parseInt(filterConfig.getInitParameter("html")); cssExp = Integer.parseInt(filterConfig.getInitParameter("css")); jsExp = Integer.parseInt(filterConfig.getInitParameter("js")); } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // 響應頭爲HTTP協議裏的,需轉換一下 HttpServletRequest request = null; HttpServletResponse response = null; try { request = (HttpServletRequest) req; response = (HttpServletResponse) resp; } catch (Exception e) { throw new RuntimeException("not-http request or response"); } //根據請求資源的後綴名肯定緩存時間 String uri = request.getRequestURI(); String extName = uri.substring(uri.lastIndexOf(".")+1); long expTime = 0; if("html".equals(extName)){ expTime = System.currentTimeMillis()+htmlExp*60*60*1000; }else if("css".equals(extName)){ expTime = System.currentTimeMillis()+cssExp*60*60*1000; }else if("js".equals(extName)){ expTime = System.currentTimeMillis()+jsExp*60*60*1000; } response.setDateHeader("Expires", expTime); chain.doFilter(request, response); } public void destroy() { } } ------------------------web.xml配置------------------------ <filter> <filter-name>SetCacheExpiresFilter</filter-name> <filter-class>org.flyne.examples.SetCacheExpiresFilter</filter-class> <init-param> <param-name>html</param-name> <param-value>1</param-value> </init-param> <init-param> <param-name>css</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>js</param-name> <param-value>3</param-value> </init-param> </filter> <filter-mapping> <filter-name>SetCacheExpiresFilter</filter-name> <url-pattern>*.html</url-pattern> <url-pattern>*.css</url-pattern> <url-pattern>*.js</url-pattern> </filter-mapping>
4)AutoLoginFilter :用戶自動登陸
當用戶登錄時,將表單提交到LoginServlet處理,若是用戶勾選了記住我,則將用戶的登錄信息存入Cookie:Cookie名爲「logInfo」,值爲(用戶名的base64加密結果_密碼的md5加密結果)。下面的自動登陸過濾器基於以上信息:
public class AutoLoginFilter implements Filter { //表現層同service層打交道 private UserService service = new UserServiceImpl(); public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // 轉爲Http協議的request和response HttpServletRequest request = null; HttpServletResponse response = null; try { request = (HttpServletRequest) req; response = (HttpServletResponse) resp; } catch (Exception e) { throw new RuntimeException("not-http request or response"); } HttpSession session = request.getSession(); // 判斷用戶有沒有登陸:只管沒有登陸的 User sUser = (User) session.getAttribute("user"); if (sUser == null) { Cookie[] cookies = request.getCookies(); for (int i = 0; cookies != null && i < cookies.length; i++) { Cookie cookie = cookies[i]; //名爲logInfo的Cookie記錄了登陸信息(用戶名、密碼) if ("logInfo".equals(cookie.getName())) { String value = cookie.getValue(); //Cookie中的用戶名是通過Base64加密的,因此須要解密 String username = SecurityUtil.base64Decode(value.split("_")[0]); String password = value.split("_")[1]; //Cookie中的密碼是md5加密後的,因此第三個參數爲true User user = service.login(username, password, true); //經過則在session中設置登錄標記 if(user!=null){ session.setAttribute("user", user); } break; } } } chain.doFilter(request, response); } public void destroy() { } }
未完......
參考:MOOC 過濾器 實例來自 http://www.flyne.org/article/636/2