1. 程式人生 > >Servlet——Filter(過濾器)

Servlet——Filter(過濾器)

1. 過濾器

Filter介面中有一個doFilter方法,當我們編寫好Filter,並配置對哪個web資源進行攔截後,WEB伺服器每次在呼叫web資源的service方法之前,
都會先呼叫一下filter的doFilter方法,因此,在該方法內編寫程式碼可達到如下目的:
呼叫目標資源之前,讓一段程式碼執行。
是否呼叫目標資源(即是否讓使用者訪問web資源)。
呼叫目標資源之後,讓一段程式碼執行。
  web伺服器在呼叫doFilter方法時,會傳遞一個filterChain物件進來,filterChain物件是filter介面中最重要的一個物件,它也提供了一個
doFilter方法,開發人員可以根據需求決定是否呼叫此方法,呼叫該方法,則web伺服器就會呼叫web資源的service方法,即web資源就會被訪問,
否則web資源不會被訪問。

圖示:
在這裡插入圖片描述

2. 概念

Filter 過濾器
可以對請求(響應)進行過濾

請求 --> 過濾器1 --> 過濾器2 --> … --> 過濾器n --> 目的地(Servlet, jsp)
例如:

 package filter;

 import javax.servlet.*;
 import javax.servlet.annotation.WebFilter;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
//匹配所有路徑,所有的jsp或者servlet都得經過此過濾器,這要可以減少編寫大量重複的程式碼
@WebFilter(urlPatterns = "/*")
 public class Filter1 implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    System.out.println("進入了Filter1");
    // 設定字元編碼,在過濾中寫入編碼,省去了多次編寫同樣程式碼的過程
    servletRequest.setCharacterEncoding("utf-8");
   //轉換為子類,進而使用其方法
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    //過濾繼續執行
    filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
 }
}

3. 編寫過濾器的步驟

  1. 實現Filter介面
  2. 在類上新增@WebFilter(urlPattern=“要過濾的目標路徑”)
  3. 在doFilter方法中控制請求是否前進到下一個過濾器
filterChain.doFilter(request, response); // 呼叫此方法,表示請求繼續前進,不呼叫,請求就停止不前了

4. 匹配路徑的三種寫法

  1. 精確匹配:針對於過濾具體一個servlet
    例如: /s1 /s2 …

  2. 字首匹配:針對要同時過濾多個servlet
    例如:有兩個servlet,路徑分別是 /user/s1, /user/s2
    過濾器的匹配路徑就可以寫為: /user/ *
    / * 表示匹配此應用程式中所有路徑

  3. 字尾匹配:
    .字尾
    注意: 字尾匹配不要以/開頭, 精確匹配和字首匹配需要以/開頭
    例如
    .jsp 表示在請求到達所有jsp之前,經過此過濾器

5. 多個過濾器

使用@WebFilter去控制匹配路徑時,多個過濾器都匹配目標,執行順序和過濾器類名有關。
按類名的字母順序排序
例如在idea編譯器裡我們有filter1,filte2,過濾順序將由前之後以此進行,這個順序屬於預設順序也不是一定的,後面我們會學到在xml檔案裡修改,可以改變過濾順序。

6. 過濾器的應用

應用1: 字元編碼過濾器
在過濾器中統一呼叫 request.setCharacterEncoding方法
應用2: 登入檢查

HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
// 檢查是否有登入標記
if(session.getAttribute("isLogin") == null) { // 如果沒有找到
    request.getRequestDispatcher("/login.jsp").forward(request, servletResponse);
    return;
} else { // 找到登入標記, 放行請求,讓請求繼續前進
    filterChain.doFilter(request, servletResponse);
}

應用3: 自動登入

  1. 過濾器

  2. cookie (把使用者名稱和密碼的資訊記錄在cookie)

     package controller;
     import util.Utils;
     import javax.servlet.ServletException;
     import javax.servlet.annotation.WebServlet;
     import javax.servlet.http.*;
     import java.io.IOException;
     import java.sql.Connection;
     import java.sql.PreparedStatement;
     import java.sql.ResultSet;
    
     @WebServlet(urlPatterns = "/login")
    public class LoginServlet extends HttpServlet {
     @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     String username = req.getParameter("username");
     String password = req.getParameter("password");
    
     Connection conn = null;
     PreparedStatement stmt = null;
     ResultSet rs = null;
     try {
         conn = Utils.getConnection();
         stmt = conn.prepareStatement("select password from user where username=?");
         stmt.setString(1, username);
         rs = stmt.executeQuery();
    
         // 有沒有使用者
         if(rs.next()) {
             // 有使用者
             String dbPassword = rs.getString("password");
             // 判斷密碼是否相等
             if (dbPassword.equals(password)) {
                 HttpSession session = req.getSession();
                 session.setAttribute("isLogin", true);
                 session.setAttribute("username", username);
    
                 // 自動登入(吧使用者名稱,密碼記錄在cookie裡)
                 Cookie c1 = new Cookie("username", username);
                 Cookie c2 = new Cookie("password", password);
                 c1.setMaxAge( 3600 * 24 * 7); // 一週
                 c1.setHttpOnly(true); // 控制這個cookie不能通過js程式碼修改
                 c2.setMaxAge( 3600 * 24 * 7); // 一週
                 c2.setHttpOnly(true);
                 resp.addCookie(c1);
                 resp.addCookie(c2);
    
                 req.getRequestDispatcher("/welcome.jsp").forward(req,resp);
             } else {
                 req.getRequestDispatcher("/login.jsp").forward(req,resp);
             }
         } else { // 沒有這個使用者
             req.getRequestDispatcher("/login.jsp").forward(req,resp);
         }
     } catch (Exception e) {
         e.printStackTrace();
     } finally {
         Utils.close(rs, stmt, conn);
     }
    }
    }
    
    
    
    
      package filter;
      import util.Utils;
      import javax.servlet.*;
      import javax.servlet.annotation.WebFilter;
      import javax.servlet.http.Cookie;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      import java.io.IOException;
      import java.sql.Connection;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      /*
    檢查是否有登入標記,沒有就跳轉到登入頁面
    */
       @WebFilter(urlPatterns = "/user/*")
    public class LoginFilter implements Filter {
       @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    }
     @Override
     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(session.getAttribute("isLogin") != null) {
         // 如果登入了,放行
         filterChain.doFilter(request,response);
         return;
    
     } else {
         Cookie[] cookies = request.getCookies();
         Cookie uCookie = null; // 儲存username的cookie
         Cookie pCookie = null; // 儲存password的cookie
         for (Cookie cookie : cookies) {
             if(cookie.getName().equals("username")) {
                 uCookie = cookie;
             }
             if(cookie.getName().equals("password")) {
                 pCookie = cookie;
             }
         }
         if(uCookie == null || pCookie == null) {
             request.getRequestDispatcher("/login.jsp").forward(request,response);
             return;
         } else {
             Connection conn = null;
             PreparedStatement stmt = null;
             ResultSet rs = null;
             try {
                 conn = Utils.getConnection();
                 stmt = conn.prepareStatement("select password from user where username=?");
                 stmt.setString(1, uCookie.getValue());
                 rs = stmt.executeQuery();
                 if(rs.next()) {
                     // 使用者名稱,密碼都正確,放行請求
                     if (rs.getString("password").equals(pCookie.getValue())) {
                         filterChain.doFilter(request, response);
                     } else { // 密碼不正確
                         request.getRequestDispatcher("/login.jsp").forward(request,response);
                     }
                 } else { // 使用者沒查詢到
                     request.getRequestDispatcher("/login.jsp").forward(request,response);
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 Utils.close(rs, stmt, conn);
             }
         }
     }
         }
     @Override
     public void destroy() {
    }
    }
    

7. 密碼的加密處理

雜湊技術(簽名技術)
明文 密文
123456 ==> e10adc3949ba59abbe56e057f20f883e
md5 演算法 16位元組
sha256 演算法

特點1:運算不可逆
特點2:運算的結果長度固定
特點3:原始內容一樣,運算的結果是一樣的

8. jsp 隱式物件(9個)

config jsp的配置資訊
page jsp自身物件(this)
exception 異常物件(isErrorPage=true)

response 響應
out 響應字元流

pageContext 頁面作用域
request 請求作用域 變數型別 HttpServletRequest
session 會話作用域 (同一個客戶端的多次請求) HttpSession
application 應用作用域 變數型別 ServletContext (上下文)

他們都有如下方法
.setAttribute("名", 值);   // 設定
Object 值 = .getAttribute("名"); // 獲取
Object 值 = .removeAttribute("名"); // 刪除

作用域(Scope)

<%// 向page作用域存入內容
//    pageContext.setAttribute("name", "張三"); // 同一頁面中的資訊
    request.setAttribute("name", "李四");    // 一次請求有效的資訊(例如servlet存入結果,jsp獲取結果)
    session.setAttribute("name", "王五"); // 每個使用者(瀏覽器)獨有的資訊(例如購物車資訊)
    application.setAttribute("name", "趙六"); // 適合放全域性資訊(城市資訊, 類別資訊)
    %>

    <!-- 從page作用域獲取內容-->
    <%= pageContext.getAttribute("name")%>
    <!--  從page作用域獲取內容(推薦) -->
    ${pageScope.name}

    <!-- 從request作用域取-->
    <%= request.getAttribute("name")%>
    <!-- 從request作用域取(推薦)-->
    ${requestScope.name}

    <!-- 從session作用域取值 -->
    <%= session.getAttribute("name")%>
    <!-- 從session作用域取值(推薦) -->
    ${sessionScope.name}

    <!-- 從application作用域取值 -->
    <%= application.getAttribute("name")%>
    <!-- 從application作用域取值(推薦) -->
    ${applicationScope.name}

    <hr/>
    <!-- 在el表示式取作用域變數時,不加字首,先搜尋pageScope, 再搜尋requestScope, 繼續搜尋sessionScope, 再搜尋applicationScope-->
    ${name}

9. el表示式中的隱式物件(11個)

pageScope page作用域 型別是map集合
requestScope request作用域 型別是map集合
sessionScope 會話作用域 型別是map集合
applicationScope 應用作用域 型別是map集合

param 代表所有請求引數集合 型別是map集合
paramValues 代表所有請求引數集合(用來獲取多值引數)

cookie 代表請求中所有的cookie集合 map型別
key是cookie的name, value是cookie物件(name,value)

header 獲取請求頭的內容 型別是map集合f
key是請求頭中key的名稱
headerValues 獲取請求頭的內容(用來獲取多值)
initParam 每個jsp可以配置一個初始化引數
initParam.key
pageContext