day17過濾器 禁止緩存中文亂碼自動登錄MD5加密url級別權限控制

day17
Servlet Filter 過濾器
day18 
Servlet Listener 和 在線支付
day19
JavaMail
day20 
文件上傳和下載
JavaEE Servlet規範 描述三種技術 Servlet Filter Listener
Servlet 技術 生成動態web資源
Filter 技術 對服務器web資源進行攔截(權限控制)
* Filter 對目標資源攔截,攔截HTTP請求和HTTP響應 
* Filter 攔截本質上攔截url訪問
Filter接口中定義了三個方法
init 初始化
doFilter 執行過濾
destroy 銷燬
* Filter的doFilter方法中,傳入FilterChain參數,FilterChain 請求調用鏈,提供doFilter用於執行調用鏈下一個資源
編寫Servlet步驟
1、繼承HttpServlet
2、配置web.xml Servlet虛擬路徑
3、覆蓋doGet 和 doPost


編寫第一個Filter
1、創建hello.jsp ---- 
2、編寫類 實現Filter 接口 (實現Filter接口 init doFilter destroy )
3、在web.xml中 註冊過濾器,配置Filter攔截哪個web資源
在啓動服務器時,Filter對象被創建,執行過濾器init 方法 
配置攔截後,訪問hello.jsp --- 過濾器中doFilter方法獲得執行(每請求一次,doFilter 過濾一次)
* 沒有配置過濾器之前,直接訪問hello.jsp,當配置過濾器後,過濾器先於hello.jsp執行 ----- 此時Filter1和hello.jsp組成 請求調用鏈FilterChain 
* 攔截目標資源後,如果想目標資源執行: chain.doFilter ;如果不執行chain.doFilter 目標資源不會得到執行
destroy方法和Servlet的destroy 一樣,在正常關閉服務器情況下執行
* 對同一web資源配置多個過濾器
當配置多個Filter 攔截hello.jsp 組成新的請求調用鏈 Filter1 --- Filter2 --- hello.jsp
過濾器鏈會根據mapping 的順序組成 <filter-mapping> 順序調用過濾器執行
Filter生命週期三個方法:init doFilter destroy 
1、init初始化 ---- 在服務器啓動時執行
2、doFilter 執行過濾 ---- 訪問目標時,攔截過濾執行,每次請求執行一次
3、destroy 銷燬過濾器對象 ----- 在服務器正常關閉時調用
FileConfig 作用和ServletConfig 類似---- 爲過濾器提供初始化參數
*String getInitParameter(String name) 獲得初始化信息
*public ServletContext getServletContext() 獲得ServletContext對象 保存全局數據、讀取web資源文件
配置filter-mapping
1、對一個web資源可以配置多個過濾器
2、一個過濾器可以用來過濾多個web 資源
* 在filter-mapping中存在 <servlet-name> 過濾Servlet時,可以通過url 和 name兩種配置方式
<url-pattern>/hello</url-pattern>
<servlet-name>HelloServlet</servlet-name>
3、關於url-pattern 寫法和Servlet相同 有三種 完全匹配、目錄匹配、擴展名匹配
<dispatcher> 標籤及意義
配置在什麼情況下、在什麼調用方式下攔截目標資源
request,forward、include、error 四種方式,服務器調用資源方式
* 默認dispatcher值是request (請求時調用)
request ---- 在請求時過濾
forward ---- 在轉發時過濾
include ---- 在包含時過濾
error --- 在錯誤頁面跳轉時過濾
web.xml中filter-mapping中配置<dispatcher></dispatcher>默認爲request可以配置多個
如果想讓過濾器執行,必須滿足過濾方式 !!!! 如果forward、include 、error跳轉時執行過濾 必須配置 dispatcher
案例一:關於編碼集統一處理
功能描述:編寫 input.jsp 提供輸入姓名錶單,在Servlet獲得表單數據,將數據打印頁面上
分析:過濾器在目標資源之前執行 ,過濾器攔截多個目標資源,多個目標資源相同代碼,抽取過濾器中  ----- 經常寫代碼放入過濾器
request.setCharacterEncoding("utf-8");// 解決請求post亂碼
response.setContentType("text/html;charset=utf-8"); // 響應亂碼
將以上代碼放入EncodingFilter,配置url 爲/* 對全站web資源進行編碼集設置
複習點:get亂碼解決 第一種 配置tomcat server.xml URIEncoding="utf-8" 第二種 new String(xxx.getBytes("ISO-8859-1"),"utf-8");
* 重寫getParameter 編寫通用解決get亂碼程序
* 閱讀擴展資料 解決get post 全局亂碼過濾器
解決全站get post亂碼:
package cn.itcast.filter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Set;
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;
/**
* 解決亂碼通用的過濾器程序
* @author seawind
*/
public class EncodingFilter implements Filter {
 @Override
 public void destroy() {
 }
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  // 解決post
  request.setCharacterEncoding("utf-8");
  // 解決get
  EncodingRequest encodingRequest = new EncodingRequest(
    (HttpServletRequest) request);
  chain.doFilter(encodingRequest, response);
 }
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 }
}
class EncodingRequest extends HttpServletRequestWrapper {
 private HttpServletRequest request;
 private boolean hasEncode = false;
 public EncodingRequest(HttpServletRequest request) {
  super(request);
  this.request = request;
 }
 @Override
 public String getParameter(String name) {
  // 通過getParameterMap方法完成
  String[] values = getParameterValues(name);
  if (values == null) {
   return null;
  }
  return values[0];
 }
 @Override
 public String[] getParameterValues(String name) {
  // 通過getParameterMap方法完成
  Map<String, String[]> parameterMap = getParameterMap();
  String[] values = parameterMap.get(name);
  return values;
 }
 @Override
 public Map getParameterMap() {
  Map<String, String[]> parameterMap = request.getParameterMap();
  String method = request.getMethod();
  if (method.equalsIgnoreCase("post")) {
   return parameterMap;
  }
  // get提交方式 手動轉碼 , 這裏的轉碼只進行一次 所以通過 hasEncode 布爾類型變量控制
  if (!hasEncode) { 
   Set<String> keys = parameterMap.keySet();
   for (String key : keys) {
    String[] values = parameterMap.get(key);
    if (values == null) {
     continue;
    }
    for (int i = 0; i < values.length; i++) {
     String value = values[i];
     // 解決get
     try {
      value = new String(value.getBytes("ISO-8859-1"),
        "utf-8");
      // values是一個地址
      values[i] = value;
     } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
     }
    }
    // 一次轉碼完成後,設置轉碼狀態爲true
    hasEncode = true;
   }
  }
  return parameterMap;
 }
}
案例二:禁用緩存過濾器
分析:在web開發中,存在很多動態web資源,經常需要更新,爲了保證客戶端第一時間獲得更新後結果,禁止動態web資源緩存
禁止緩存三個頭信息
cache-control : no-cache
expires : -1  ---- 日期
pragma : no-cache
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Pragma", "no-cache");
* 禁用緩存公共代碼放入過濾器中 ------ NoCacheFilter 禁止動態資源緩存
public class NoCacheFilter implements Filter {
 @Override
 public void destroy() {
 }
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  // 使用與HTTP協議相關 API,需要將參數轉爲子類型
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  httpServletResponse.setHeader("Cache-Control", "no-cache");
  httpServletResponse.setDateHeader("Expires", -1);
  httpServletResponse.setHeader("Pragma", "no-cache");
  chain.doFilter(request, httpServletResponse);
 }
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 }
}
案例三:對於圖片一系列資源,不經常發生變動 ----- 圖片緩存過濾器
* 雖然tomcat服務器內部 對靜態資源有一個緩存策略,但是客戶端仍然需要與服務器進行通信,通信性能損失
* 如果一些圖片 永遠不變,通過Expires 字段,設置圖片 緩存時間 (當緩存時間沒有過期之前,客戶端方位資源 不會與服務器進行通信)
Expires 對於經常不變文件,是一個比tomcat默認緩存策略更優 解決方案!!!
public class ImageExpiresFilter implements Filter {
 @Override
 public void destroy() {
 }
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  // 設置圖片過期時間 ,設置Expires頭信息
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  // 過期時間 = 當前時間 + 還有多久過期
  Calendar calendar = Calendar.getInstance();
  calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
  httpServletResponse
    .setDateHeader("Expires", calendar.getTimeInMillis());// 過期時間一個月
  httpServletResponse.setDateHeader("Expires", System.currentTimeMillis()
    + 1000L * 60 * 60 * 24 * 30);// 過期時間一個月
  chain.doFilter(request, httpServletResponse);
 }
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 }
}
--------------------------------------------------------------------------------------------------------------------------------
案例四:自動登陸過濾器 
功能:第一次登陸時,勾選自動登陸,關閉瀏覽器,再次打開,系統會自動進行用戶已經登陸狀態
登陸功能
create table user(
   id int primary key auto_increment,
   username varchar(40),
   password varchar(40),
   role varchar(40)
);
insert into user values(null,'aaa','111','admin');
insert into user values(null,'bbb','111','user');
insert into user values(null,'ccc','111','user');
* 複製mysql驅動、c3p0、dbutils 到WEB-INF/lib 下
* 複製JDBCUtils 工具類 和c3p0-config.xml
轉發和重定向使用
1、使用轉發,通過request.setAttribute 傳遞數據給 頁面,路徑不要寫/工程名 ;
2、使用重定向,不能用request傳遞數據,路徑 要寫 /工程名
AutoLoginFilter 在什麼情況下需要自動登陸 ??
1、當前用戶必須未登陸
2、存在自動登陸cookie信息
* 自動登陸過濾器,過濾頁面一定不包括登陸和註冊 頁面
自動登陸案例問題:
1、 如果用戶名中文怎麼辦?
insert into user values(null,'小紅','123','user');
解決方案:保存cookie時進行手動編碼 URL編碼 
生成cookie 時 new Cookie(URLEncoder.encode(用戶名,"utf-8"));  ------ LoginServlet
讀取cookie時, 對用戶進行解碼 URLDecoder.decode(用戶名,"utf-8");  ---- AutoLoginFilter
2、 中文名和密碼安全問題
* cookie 是一個文本類型文件,內容非常容易泄露
實際開發中數據庫中密碼和cookie中密碼 都應該 加密後保存  ---- 單向加密算法 MD5
* MySQL 提供md5 方法進行加密
將數據進行md5 加密
update user set password = md5(password) ;
MD5 破解  ----- 多對一映射算法 (運行兩個不同明文 產生相同密文  概率很低) ---- 王小云 並沒有提供解密算法,提供一套提高碰撞機率算法
* http://www.bizhizu.cn/
Base64 編解碼技術,用於加密網絡傳輸中安全內容 ---- 例如:迅雷下載地址
* 很多協議內容傳輸都採用BASE64 編碼
public class DigestUtils {
 public static void main(String[] args) {
  // 迅雷地址獲得 --- 使用Base64
  String url = "www.huiyi8.com/素材中國1.rmvb";
  // 在地址前後添加 AA和ZZ
  url = "AA" + url + "ZZ";
  // 進行base64編碼
  url = base64encode(url);
  // 在路徑前 thunder://
  url = "thunder://" + url;
  System.out.println(url);
 }
 /**
  * Base64編碼
  */
 public static String base64encode(String message) {
  BASE64Encoder base64Encoder = new BASE64Encoder();
  return base64Encoder.encode(message.getBytes());
 }
 /**
  * Base64解碼
  * 
  * @throws IOException
  */
 public static String base64decode(String message) throws IOException {
  BASE64Decoder base64Decoder = new BASE64Decoder();
  byte[] buf = base64Decoder.decodeBuffer(message);
  return new String(buf);
 }
 /**
  * 使用md5的算法進行加密
  * 
  * @param plainText
  *            加密原文
  * @return 加密密文
  */
 public static String md5(String plainText) {
  byte[] secretBytes = null;
  try {
   secretBytes = MessageDigest.getInstance("md5").digest(
     plainText.getBytes());
  } catch (NoSuchAlgorithmException e) {
   throw new RuntimeException("沒有md5這個算法!");
  }
  return new BigInteger(1, secretBytes).toString(16);// 將加密後byte數組
  // 轉換16進製表示
 }
}
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  // 1、判斷當前用戶是否已經登陸
  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  if (httpServletRequest.getSession().getAttribute("user") == null) {
   // 未登錄
   // 2、判斷autologin對應cookie 是否存在 ---- 將cookie 查詢寫爲工具類
   Cookie cookie = CookieUtils.findCookie(httpServletRequest
     .getCookies(), "autologin");
   if (cookie != null) {
    // 找到了自動登陸cookie
    String username = cookie.getValue().split("\\.")[0];// 如果用戶名中文,需要解密
    username = URLDecoder.decode(username, "utf-8");
    String password = cookie.getValue().split("\\.")[1];
    // 獲得就是md5 加密密碼,此處無需加密
    // 登陸邏輯
    String sql = "select * from user where username=? and password = ?";
    Object[] args = { username, password };
    QueryRunner queryRunner = new QueryRunner(JDBCUtils
      .getDataSource());
    try {
     User user = queryRunner.query(sql, new BeanHandler<User>(
       User.class), args);
     if (user != null) {
      httpServletRequest.getSession().setAttribute("user",
        user);
     }
     chain.doFilter(httpServletRequest, response);
    } catch (SQLException e) {
     e.printStackTrace();
    }
   } else {
    // 沒有自動登陸信息
    chain.doFilter(httpServletRequest, response);
   }
  } else {
   // 已經登陸,不需要自動登陸
   chain.doFilter(httpServletRequest, response);
  }
 }
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 }
}
public class LoginServlet extends HttpServlet {
 public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  // 獲得用戶名和密碼
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  // MD5 加密
  password = DigestUtils.md5(password);
  // 查詢數據庫
  String sql = "select * from user where username = ? and password= ?";
  Object[] args = { username, password };
  QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
  try {
   User user = queryRunner.query(sql,
     new BeanHandler<User>(User.class), args);
   if (user == null) {
    // 查詢用戶不存在,登陸失敗
    request.setAttribute("msg", "用戶名或者密碼錯誤");
    request.getRequestDispatcher("/demo4/login.jsp").forward(
      request, response);
   } else {
    // 登陸成功
    // 判斷是否勾選自動登陸
    if ("true".equals(request.getParameter("autologin"))) {
     // 用戶勾選了 自動登陸
     // 將用戶名和密碼 以cookie形式寫給客戶端
     Cookie cookie = new Cookie("autologin", URLEncoder.encode(
       username, "utf-8")
       + "." + password);
     cookie.setMaxAge(60 * 60);
     cookie.setPath("/day17");
     response.addCookie(cookie);
    }
    // 將成功信息 保存Session
    request.getSession().setAttribute("user", user); // 表示已經登陸
    response.sendRedirect("/day17/demo4/welcome.jsp");
   }
  } catch (SQLException e) {
   e.printStackTrace();
  }
 }
 public void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  doGet(request, response);
 }
}


案例五:關於URL級別權限控制
一個軟件系統功能非常多,將功能進行分類(根據用戶角色),將不同功能放入不同url 目錄中 ,對目錄級別進行url 權限控制
/public/ 代表遊客可以訪問 : 用戶註冊、用戶登錄
/user/ 代表普通用戶可以訪問: 購買商品,查看購物車,訂單支付
/admin 代表超級管理員可以訪問:查看商品銷售情況,添加商品
建立JSP,在index.jsp提供對八個功能jsp訪問 鏈接
編寫登陸功能
仍然使用 day17 數據庫 下 user 表
* 權限控制時,登陸功能遊客可以訪問 ----- 已經將login.jsp 放入public 但是 LoginServlet 也是登陸程序 也路徑放入public
* LoginServlet 配置url-pattern 必須爲/public/login
編寫PrivilegeFilter 對資源進行全局權限控制
* 重點:
1、 獲得訪問資源路徑 
String path = httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length());
2、 判斷當前用戶是否登陸
User user = (User) httpServletRequest.getSession().getAttribute("user");
   if (user == null) {
    // 未登陸
   } else {
    // 已經登陸
   }
* 過濾掉未登錄不能訪問資源
3、獲得用戶權限,判斷權限可以訪問哪些資源  user.getRole()


今天學習重點內容:
1、過濾器原理
2.過濾器5個案例 
案例一、案例二、案例三 重點原理 --- 代碼很少
案例四 自動登陸 (原理) --- 會寫
案例五 URL級別權限控制 (重點!!!!!!)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章