Servlet Filter

1、Filter簡介

1Filter也稱之爲過濾器,它是Servlet技術中最實用的技術,WEB開發人員通過Filter技術,對web服務器管理的所有web資源:例如Jsp, Servlet, 靜態圖片文件或靜態 html 文件等進行攔截,從而實現一些特殊的功能。例如實現URL級別的權限訪問控制、過濾敏感詞彙、壓縮響應信息等一些高級功能。

2Servlet API中提供了一個Filter接口,開發web應用時,如果編寫的Java類實現了這個接口,則把這個java類稱之爲過濾器Filter。通過Filter技術,開發人員可以實現用戶在訪問某個目標資源之前,對訪問的請求和響應進行攔截,如下所示:

2Filter是如何實現攔截的?

Filter接口中有一個doFilter方法,當開發人員編寫好Filter,並配置對哪個web資源(攔截url)進行攔截後,WEB服務器每次在調用web資源之前,都會先調用一下filterdoFilter方法,因此,在該方法內編寫代碼可達到如下目的:

調用目標資源之前,讓一段代碼執行

是否調用目標資源(即是否讓用戶訪問web資源)。

web服務器在調用doFilter方法時,會傳遞一個filterChain對象進來,filterChain對象是filter接口中最重要的一個對象,它也提供了一個doFilter方法,開發人員可以根據需求決定是否調用此方法,調用該方法,則web服務器就會調用web資源的service方法,即web資源就會被訪問,否則web資源不會被訪問。

調用目標資源之後,讓一段代碼執行

3、Filter開發入門

1Filter開發分爲二個步驟:

編寫java類實現Filter接口,並實現(三個方法)doFilter方法。

在 web.xml 文件中使用<filter><filter-mapping>元素對編寫的filter類進行註冊,並設置它所能攔截的資源。

2Filter鏈 --- 

在一個web應用中,可以開發編寫多個Filter,這些Filter組合起來稱之爲一個Filter鏈。

web服務器根據Filterweb.xml文件中的註冊順序<mapping>,決定先調用哪個Filter,當第一個FilterdoFilter方法被調用時,web服務器會創建一個代表Filter鏈的FilterChain對象傳遞給該方法。在doFilter方法中,開發人員如果調用了FilterChain對象的doFilter方法,則web服務器會檢查FilterChain對象中是否還有filter,如果有,則調用第2filter,如果沒有,則調用目標資源。

Filter鏈實驗(查看filterChain API文檔)

4、Filter的生命週期

1init(FilterConfig filterConfig)throws ServletException

和我們編寫的Servlet程序一樣,Filter的創建和銷燬由WEB服務器負責。 web 應用程序啓動時,web 服務器將創建Filter 的實例對象,並調用其init方法進行初始化(注:filter對象只會創建一次,init方法也只會執行一次。示例 )

開發人員通過init方法的參數,可獲得代表當前filter配置信息的FilterConfig對象。

2doFilter(ServletRequest,ServletResponse,FilterChain)

每次filter進行攔截都會執行

在實際開發中方法中參數requestresponse通常轉換爲HttpServletRequestHttpServletResponse類型進行操作

3destroy()

Web容器卸載 Filter 對象之前被調用。

package com.itheima.filter;

import java.io.IOException;

import java.util.Enumeration;

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 com.sun.net.httpserver.Filter.Chain;

public class Filter1 implements Filter {

public Filter1() {

System.out.println("Filter被創建出來了。。。");

}

public void destroy() {

System.out.println("destory......");

}

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

System.out.println("dofilter....");

chain.doFilter(request, response);

}

public void init(FilterConfig filterConfig) throws ServletException {

System.out.println("init.....");

String value1 = filterConfig.getInitParameter("param1");

System.out.println(value1);

Enumeration enumeration =filterConfig.getInitParameterNames();

while(enumeration.hasMoreElements()){

String name = (String) enumeration.nextElement();

String value = filterConfig.getInitParameter(name);

System.out.println(name+":"+value);

}

filterConfig.getServletContext();

}

}

 5FilterConfig接口

1用戶在配置filter時,可以使用<init-param>filter配置一些初始化參數,當web容器實例化Filter對象,調用其init方法時,會把封裝了filter初始化參數的filterConfig對象傳遞進來。因此開發人員在編寫filter時,通過filterConfig對象的方法,就可獲得:

String getFilterName():得到filter的名稱。

String getInitParameter(String name): 返回在部署描述中指定名稱的初始化參數的值。如果不存在返回null.

Enumeration getInitParameterNames():返回過濾器的所有初始化參數的名字的枚舉集合。

public ServletContext getServletContext():返回Servlet上下文對象的引用。

2FilterConfig 提供參數,是Filter類私有參數,Filter2的初始化參數,不能在Filter1 中進行獲取 

3 配置全局參數,<context-param> 進行配置,通過ServletContext 獲得 

4實驗:得到filter配置信息

6、註冊Filter

<filter>

      <filter-name>testFitler</filter-name>

     <filter-class>org.test.TestFiter</filter-class>

     <init-param>

 <param-name>word_file</param-name>

 <param-value>/WEB-INF/word.txt</param-value>

     </init-param>

</filter>

<filter-name>用於爲過濾器指定一個名字,該元素的內容不能爲空。

<filter-class>元素用於指定過濾器的完整的限定類名。

<init-param>元素用於爲過濾器指定初始化參數,它的子元素<param-name>指定參數的名字,<param-value>指定參數的值。在過濾器中,可以使用FilterConfig接口對象來訪問初始化參數。

7、映射Filter

<filter-mapping>元素用於設置一個 Filter 所負責攔截的資源。一個Filter攔截的資源可通過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑

1<filter-name>子元素用於設置filter的註冊名稱。該值必須是在<filter>元素中聲明過的過濾器的名字

2<url-pattern>設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)

3<servlet-name>指定過濾器所攔截的Servlet名稱。

4<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,可以是REQUEST,INCLUDE,FORWARDERROR之一,默認REQUEST。用戶可以設置多個<dispatcher> 子元素用來指定 Filter 對資源的多種調用方式進行攔截。

5<filter-mapping> 過濾器攔截配置

如果連接目標資源是一個Servlet,可以選擇urlservlet名稱兩種配置方式 

<!-- 攔截/helloServlet 路徑 -->

<url-pattern>/hello</url-pattern>

<!-- 攔截Servlet 還可以通過Servlet 名稱進行攔截 -->

<servlet-name>HelloServlet</servlet-name>

6url-pattern 和 Servlet中路徑寫法一樣,有三種 : 完全匹配、目錄匹配、擴展名匹配

7<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式

容器調用服務器端資源 有四種方式 

REQUESTFORWARDINCLUDEERROR

8、映射Filter的多種方式

<dispatcher> 子元素可以設置的值及其意義:

REQUEST:當用戶直接訪問頁面時,Web容器將會調用過濾器。如果目標資源是通過RequestDispatcherinclude()forward()方法訪問時,那麼該過濾器就不會被調用。

INCLUDE:如果目標資源是通過RequestDispatcherinclude()方法訪問時,那麼該過濾器將被調用。除此之外,該過濾器不會被調用。

FORWARD:如果目標資源是通過RequestDispatcherforward()方法訪問時,那麼該過濾器將被調用,除此之外,該過濾器不會被調用。

ERROR:如果目標資源是通過聲明式異常處理機制調用時,那麼該過濾器將被調用。除此之外,過濾器不會被調用

9、映射Filter示例

<filter-mapping>

     <filter-name>testFilter</filter-name>

    <url-pattern>/test.jsp</url-pattern>

</filter-mapping>

<filter-mapping>

    <filter-name>testFilter</filter-name>

   <url-pattern>/index.jsp</url-pattern>

   <dispatcher>REQUEST</dispatcher>

   <dispatcher>FORWARD</dispatcher>

</filter-mapping>

10Filter常見應用

1統一全站字符編碼的過濾器

通過配置參數encoding指明使用何種字符編碼,以處理Html Form請求參數的中文問題

案例:編寫jsp 輸入用戶名,在Servlet中獲取用戶名,將用戶名輸出到瀏覽器上 

處理請求post亂碼代碼

request.setCharacterEncoding("utf-8");

設置響應編碼集代碼

response.setContentType("text/html;charset=utf-8");

經常會使用,而過濾器可以在目標資源之前執行,將很多程序中處理亂碼公共代碼,提取到過濾器中 ,以後程序中不需要處理編碼問題了 

package com.itheima.filter;

import java.io.IOException;

import java.io.UnsupportedEncodingException;

import java.util.Map;

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;

import javax.servlet.http.HttpServletRequestWrapper;

public class EncodingFilter implements Filter {

private FilterConfig config = null;

public void destroy() {

}

/**

 * 全站亂碼解決

 */

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest req = (HttpServletRequest) request;

chain.doFilter(new MyRequest(req), response);

}

class MyRequest extends HttpServletRequestWrapper {

private HttpServletRequest request = null;

private boolean isEncode = false;

public MyRequest(HttpServletRequest request) {

super(request);

this.request = request;

}

@Override

public Map getParameterMap() {

if (request.getMethod().equalsIgnoreCase("post")) {

try {

request.setCharacterEncoding(config.getInitParameter("encode"));

return request.getParameterMap();

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

}

} else if (request.getMethod().equalsIgnoreCase("get")) {

try {

Map<String, String[]> map = request.getParameterMap();

if(!isEncode){

for (String key : map.keySet()) {

String [] vs = map.get(key);

for(int i=0;i<vs.length;i++){

String v = vs[i];

v = new String(v.getBytes("iso8859-1"), config.getInitParameter("encode"));

vs[i] = v;

}

}

isEncode = true;

}

return map;

} catch (Exception e) {

e.printStackTrace();

}

}

return super.getParameterMap();

}

@Override

public String[] getParameterValues(String name) {

if(getParameterMap().get(name)==null)return null;

return (String[])getParameterMap().get(name);

}

@Override

public String getParameter(String name) {

if(getParameterMap().get(name)==null)return null;

return ((String[])getParameterMap().get(name))[0];

}

}

public void init(FilterConfig filterConfig) throws ServletException {

this.config = filterConfig;

}

}

package com.itheima.web;

import java.io.IOException;

import java.util.Map;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class EncodeTestServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String vs = request.getParameterValues("username")[0];

System.out.println(vs);

String value = request.getParameter("username");

System.out.println(value);

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

<filter>

<filter-name>encode</filter-name>

<filter-class>com.itheima.filter.EncodingFilter</filter-class>

<init-param>

<param-name>encode</param-name>

<param-value>utf-8</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>encode</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

2禁止瀏覽器緩存所有動態頁面的過濾器:

有 個 HTTP 響應頭字段都可以禁止瀏覽器緩存當前頁面,它們在 Servlet 中的示例代碼如下:

response.setDateHeader("Expires",-1);

response.setHeader("Cache-Control","no-cache"); 

response.setHeader("Pragma","no-cache"); 

並不是所有的瀏覽器都能完全支持上面的三個響應頭,因此最好是同時使用上面的三個響應頭。

Expires數據頭:值爲GMT時間值,爲-1指瀏覽器不要緩存頁面

Cache-Control響應頭有兩個常用值: 

no-cache指瀏覽器不要緩存當前頁面。

max-age:xxx指瀏覽器緩存頁面xxx秒。

package com.itheima.filter;

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;

import javax.servlet.http.HttpServletResponse;

public class NoChachFilter implements Filter {

public void destroy() {

// TODO Auto-generated method stub

}

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

HttpServletResponse resp = (HttpServletResponse) response;

  resp.setDateHeader("Expires",-1);

  resp.setHeader("Cache-Control","no-cache");

  resp.setHeader("Pragma","no-cache");

chain.doFilter(request, response);

}

public void init(FilterConfig filterConfig) throws ServletException {

// TODO Auto-generated method stub

}

}

<filter>

<filter-name>nocach</filter-name>

<filter-class>com.itheima.filter.NoChachFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>nocach</filter-name>

<url-pattern>*.jsp</url-pattern>

</filter-mapping>

3控制瀏覽器緩存頁面中的靜態資源的過濾器:

場景:有些動態頁面中引用了一些圖片或css文件以修飾頁面效果,這些圖片和css文件經常是不變化的,所以爲減輕服務器的壓力,可以使用filter控制瀏覽器緩存這些文件,以提升服務器的性能。

Tomcat緩存策略 

對於服務器端經常不變化文件,設置客戶端緩存時間,在客戶端資源緩存時間到期之前,就不會去訪問服務器獲取該資源 -------- tomcat內置緩存策略更優手段 

減少服務器請求次數,提升性能 

設置靜態資源緩存時間,需要設置 Expires 過期時間 ,在客戶端資源沒有過期之前,不會產生對該資源的請求的 

設置Expires 通常使用 response.setDateHeader 進行設置 設置毫秒值 

package com.itheima.filter;

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;

import javax.servlet.http.HttpServletResponse;

public class CacheFilter implements Filter {

public void destroy() {

// TODO Auto-generated method stub

}

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

HttpServletResponse resp = (HttpServletResponse) response;

resp.setDateHeader("Expires", System.currentTimeMillis()+3600l*24*30*1000);

chain.doFilter(request, response);

}

public void init(FilterConfig filterConfig) throws ServletException {

// TODO Auto-generated method stub

}

}

<filter>

<filter-name>cache</filter-name>

<filter-class>com.itheima.filter.CacheFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>cache</filter-name>

<url-pattern>*.jpg</url-pattern>

<url-pattern>*.gif</url-pattern>

<url-pattern>*.png</url-pattern>

<dispatcher>REQUEST</dispatcher>

</filter-mapping>

4實現用戶自動登陸的過濾器

在用戶登陸成功後,以cookis形式發送用戶名、密碼給客戶端

編寫一個過濾器,filter方法中檢查cookie中是否帶有用戶名、密碼信息,如果存在則調用業務層登陸方法,登陸成功後則向session中存入user對象(即用戶登陸標記),以實現程序完成自動登陸。

在訪問一個站點,登陸時勾選自動登陸(三個月內不用登陸),操作系統後,關閉瀏覽器;過幾天再次訪問該站點時,直接進行登陸後狀態 

在數據庫中創建 user

create table user (

   id int primary key auto_increment,

   username varchar(20),

   password varchar(40),

   role varchar(10)

);

insert into user values(null,'admin','123','admin');

insert into user values(null,'aaa','123','user');

insert into user values(null,'bbb','123','user');

自動登陸 :未登錄、存在自動登陸信息、自動登陸信息正確 

在用戶完成登陸後,勾選自動登陸複選框,服務器端將用戶名和密碼 以Cookie形式,保存在客戶端 。當用戶下次訪問該站點,AutoLoginFilter 過濾器從Cookie中獲取 自動登陸信息 

1、判斷用戶是否已經登陸,如果已經登陸,沒有自動登陸的必要

2、判斷Cookie中是否含有自動登陸信息 ,如果沒有,無法完成自動登陸

3、使用cookie用戶名和密碼 完成自動登陸 

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

  </head>

  <body>

   <form action="${pageContext.request.contextPath }/servlet/LoginServlet2" method="post">

   用戶名<input type="text" name="username"/>

   密碼<input type="text" name="password"/>

   <input type="checkbox" name="autoLogin" value="true" />一個月內自動登錄

   <input type="submit" value="登錄"/>

   </form>

  </body>

</html>

package com.itheima.autologin;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.dbutils.QueryRunner;

import org.apache.commons.dbutils.handlers.BeanHandler;

import com.itheima.domain.User;

import com.itheima.util.DaoUtil;

import com.itheima.util.MD5Util;

public class LoginServlet2 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

try{

//1.校驗用戶名密碼是否正確

QueryRunner runner = new QueryRunner(DaoUtil.getSource());

User user = runner.query("select * from user where username=? and password=?", new BeanHandler<User>(User.class),request.getParameter("username"),MD5Util.md5(request.getParameter("password")));

if(user == null){

request.setAttribute("msg", "用戶名密碼不正確!!");

request.getRequestDispatcher("/login.jsp").forward(request, response);

return;

}else{

//用戶名密碼都正確,在session域中保存用戶的登錄狀態

request.getSession().setAttribute("user", user);

//如果勾選過一個月內自動登錄,發送cookie信息給瀏覽器,使瀏覽器保存用戶名密碼一個月

if(request.getParameter("autoLogin")!=null){

Cookie cookie = new Cookie("autologin",user.getUsername()+":"+user.getPassword());

cookie.setMaxAge(3600*24*30);

cookie.setPath(request.getContextPath());

response.addCookie(cookie);

}

response.sendRedirect(request.getContextPath()+"/autologin/homepage.jsp");

}

}catch (Exception e) {

e.printStackTrace();

}

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

  </head>

  <body>

   <c:if test="${sessionScope.user == null}">

   歡迎光臨遊客!<a href="${pageContext.request.contextPath }/autologin/login.jsp">登錄</a>

   </c:if>

   <c:if test="${sessionScope.user != null}">

   歡迎回來

   </c:if>

  </body>

</html>

package com.itheima.filter;

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;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.dbutils.QueryRunner;

import org.apache.commons.dbutils.handlers.BeanHandler;

import com.itheima.domain.User;

import com.itheima.util.DaoUtil;

public class AutoLoginFilter implements Filter {

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest req = (HttpServletRequest) request;

//1.檢查用戶是否已經登錄

if(req.getSession(false) == null || req.getSession().getAttribute("user")==null){

//2.如果用戶沒有登錄過,則檢查是否帶了autologincookie

Cookie [] cs = req.getCookies();

Cookie findc = null;

if(cs!=null){

for(Cookie c : cs){

if(c.getName().equals("autologin")){

findc = c;

break;

}

}

}

if(findc != null){

//3.如果有autologin cookie,獲取cookie的值,檢查用戶名密碼是否正確

String username = findc.getValue().split(":")[0];

String password = findc.getValue().split(":")[1];

//4.如果用戶名密碼都正確,則自動登錄一把

try{

QueryRunner runner = new QueryRunner(DaoUtil.getSource());

User user = runner.query("select * from user where username=? and password=?", new BeanHandler<User>(User.class),username,password);

if(user!=null){

req.getSession().setAttribute("user", user);

}

}catch (Exception e) {

e.printStackTrace();

}

}

}

//5.放行資源

chain.doFilter(request, response);

}

public void init(FilterConfig filterConfig) throws ServletException {

// TODO Auto-generated method stub

}

}

<filter>

<filter-name>autologinFilter</filter-name>

<filter-class>com.itheima.filter.AutoLoginFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>autologinFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

5使用Filter實現URL級別的權限認證

1情景:在實際開發中我們經常把一些執行敏感操作的servlet映射到一些特殊目錄中,並用filter把這些特殊目錄保護起來,限制只能擁有相應訪問權限的用戶才能訪問這些目錄下的資源。從而在我們系統中實現一種URL級別的權限功能。

要求:爲使Filter具有通用性,Filter保護的資源和相應的訪問權限通過filter參數的形式予以配置。

2系統中存在很多資源,將需要進行權限控制的資源,放入特殊路徑中,編寫過濾器管理訪問特殊路徑的請求,如果沒有相應身份和權限,控制無法訪問 

認證:who are you ? 用戶身份的識別 ------------ 登陸功能

權限:以認證爲基礎 what can you do ? 您能做什麼? 必須先登陸,纔有身份,有了身份,才能確定可以執行哪些操作 

package com.itheima.filter;

import java.io.IOException;

import java.util.Enumeration;

import java.util.HashMap;

import java.util.Map;

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;

import javax.servlet.http.HttpSession;

import com.itheima.domain.User;

public class PrivilegeFilter implements Filter {

private FilterConfig config = null;

private Map<String, String> map = new HashMap<String, String>();

public void destroy() {

// TODO Auto-generated method stub

}

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

//1.當前訪問的資源是否需要權限(當前訪問資源路徑是否是map中具有的需要權限控制的路徑的子路徑)

HttpServletRequest req = (HttpServletRequest) request;

String uri = req.getRequestURI();

uri = uri.substring(config.getServletContext().getContextPath().length());

String privilege = null;

for(String name : map.keySet()){

if(uri.startsWith(name)){

privilege = map.get(name);

}

}

if(privilege == null){

//2.如果不需要權限,直接放行

chain.doFilter(request, response);

return;

}else{//3.如果需要權限,判斷當前用戶具有的權限和訪問該資源需要的權限是否相匹配,如果匹配就放行,如果不匹配則提示

HttpSession  session = req.getSession(false);

if(session == null || session.getAttribute("user")==null){

throw new RuntimeException("請先登錄");

}

User user = (User) session.getAttribute("user");

if(user.getRole().equals(privilege)){

chain.doFilter(request, response);

return;

}else{

throw new RuntimeException("沒有對應的權限!!!!");

}

}

}

public void init(FilterConfig filterConfig) throws ServletException {

this.config = filterConfig;

Enumeration enumeration = config.getInitParameterNames();

while(enumeration.hasMoreElements()){

String name = (String) enumeration.nextElement();

String value = config.getInitParameter(name);

map.put(name, value);

}

}

}

<filter>

<filter-name>PrivilegeFilter</filter-name>

<filter-class>com.itheima.filter.PrivilegeFilter</filter-class>

<init-param>

<param-name>/admin</param-name>

<param-value>admin</param-value>

</init-param>

<init-param>

<param-name>/user</param-name>

<param-value>user</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>PrivilegeFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

11、MD5加密

/**

 * 使用md5的算法進行加密

 */

public static String md5(String plainText) {

byte[] secretBytes = null;

try {

secretBytes = MessageDigest.getInstance("md5").digest(

plainText.getBytes());

} catch (NoSuchAlgorithmException e) {

throw new RuntimeException("沒有md5這個算法!");

}

String md5code = new BigInteger(1, secretBytes).toString(16);

for (int i = 0; i < 32 - md5code.length(); i++) {

md5code = "0" + md5code;

}

return md5code;

}

如果將用戶密碼保存在cookie文件中,非常不安全的 ,通常情況下密碼需要加密後才能保存到客戶端 

使用md5算法對密碼進行加密 

* md5 加密算法是一個單向加密算法 ,支持明文---密文 不支持密文解密 

MySQL數據庫中提供md5 函數,可以完成md5 加密

mysql> select md5('123'); 

+----------------------------------+

| md5('123')                       |

+----------------------------------+

| 202cb962ac59075b964b07152d234b70 |

+----------------------------------+

解密後結果是32位數字 16進製表示 

Java中提供類 MessageDigest 完成MD5加密

------------------------------------------------------------------

將數據表中所有密碼 變爲密文 update user set password = md5(password) ;

Demo4Servlet 登陸邏輯中,對密碼進行md5 加密 

AutoLoginFilter 因爲從Cookie中獲得就是加密後密碼,所以登陸時無需再次加密 

------------------------------------------------------------------

MD5 2004 年被王小云破解,md5算法是多對一加密算法,出現兩個加密後相同密文的明文很難發現 ,王小云並沒有研究出md5 解密算法,研究出一種提高碰撞概率的算法 

12Filter高級開發

1由於開發人員在filter中可以得到代表用戶請求和響應的requestresponse對象,因此在編程中可以使用Decorator(裝飾器)模式對requestresponse對象進行包裝,再把包裝對象傳給目標資源,從而實現一些特殊需求。

2Decorator設計模式的實現

1.首先看需要被增強對象繼承了什麼接口或父類,編寫一個類也去繼承這些接口或父類。

2.在類中定義一個變量,變量類型即需增強對象的類型。

3.在類中定義一個構造函數,接收需增強的對象。

4.覆蓋需增強的方法,編寫增強的代碼。

3Decorator模式 

1、包裝類需要和被包裝對象 實現相同接口,或者繼承相同父類

2、包裝類需要持有 被包裝對象的引用 

在包裝類中定義成員變量,通過包裝類構造方法,傳入被包裝對象 

3、在包裝類中,可以控制原來那些方法需要加強

不需要加強 ,調用被包裝對象的方法

需要加強,編寫增強代碼邏輯 

ServletRequestWrapper 和 HttpServletRequestWrapper 提供對request對象進行包裝的方法,但是默認情況下每個方法都是調用原來request對象的方法,也就是說包裝類並沒有對request進行增強 

在這兩個包裝類基礎上,繼承HttpServletRequestWrapper ,覆蓋需要增強的方法即可 

13request對象的增強

1Servlet API 中提供了一個request對象的Decorator設計模式的默認實現類HttpServletRequestWrapper , (HttpServletRequestWrapper 類實現了request 接口中的所有方法,但這些方法的內部實現都是僅僅調用了一下所包裝的的 request 對象的對應方法)以避免用戶在對request對象進行增強時需要實現request接口中的所有方法。

2使用Decorator模式包裝request對象,完全解決getpost請求方式下的亂碼問題

14response對象的增強

Servlet  API 中提供了response對象的Decorator設計模式的默認實現類HttpServletResponseWrapper , (HttpServletResponseWrapper類實現了response接口中的所有方法,但這些方法的內部實現都是僅僅調用了一下所包裝的的 response對象的對應方法)以避免用戶在對response對象進行增強時需要實現response接口中的所有方法。

15、response增強案例—壓縮響應

應用HttpServletResponseWrapper對象,壓縮響應正文內容。思路:

通過filter向目標頁面傳遞一個自定義的response對象。

在自定義的response對象中,重寫getOutputStream方法和getWriter方法,使目標資源調用此方法輸出頁面內容時,獲得的是我們自定義的ServletOutputStream對象。

在我們自定義的ServletOuputStream對象中,重寫write方法,使寫出的數據寫出到一個buffer中。

當頁面完成輸出後,在filter中就可得到頁面寫出的數據,從而我們可以調用GzipOuputStream對數據進行壓縮後再寫出給瀏覽器,以此完成響應正文件壓縮功能。

複習:Tomcat服務器內,提供對響應壓縮 配置實現 

conf/server.xml 中 

<Connector port="80" protocol="HTTP/1.1" 

               connectionTimeout="20000" 

               redirectPort="8443"/> 添加 compressableMimeType 和 compression

沒有壓縮 :  00:00:00.0000.0637553GET200text/htmlhttp://localhost/

<Connector port="80" protocol="HTTP/1.1" 

               connectionTimeout="20000" 

               redirectPort="8443" compressableMimeType="text/html,text/xml,text/plain" compression="on"/>

壓縮後 :  00:00:00.0000.1712715GET200text/htmlhttp://localhost/ 

Content-Encoding: gzip

Content-Length : 2715

實際開發中,很多情況下,沒有權限配置server.xml  ,無法通過tomcat配置開啓gzip 壓縮

編寫過濾器對響應數據進行gzip壓縮 

flush 方法

只有沒有緩衝區字節流,FileOutputStream 不需要flush 

而字節數組ByteArrayOutputStream、字節包裝流、字符流 需要flush ----- 這些流在調用close方法時都會自動flush 

發佈了56 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章