首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> JSP 程式設計 >> JSP 入門

JSP Filter 過濾器完整指南

     編輯:JSP 入門

JSP/Servlet Filter 過濾器完整指南

Filter 是實作 javax.servlet.Filter 介面的 Java 類別,能在請求到達 Servlet/JSP 前、或回應離開伺服器前攔截處理。常用於編碼設定、登入驗證、日誌記錄、壓縮、XSS 防護等橫切關注點。


Filter 生命週期與核心方法

方法呼叫時機用途
init(FilterConfig config)Web 應用啟動時,僅一次讀取初始化參數、建立資源連線
doFilter(req, resp, chain)每次請求匹配時核心邏輯:預處理 → chain.doFilter() → 後處理
destroy()Web 應用關閉/重新部署釋放資源、關閉連線

基本實作範例:記錄請求日誌

package com.example.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.LocalDateTime;

@WebFilter("/*") // 攔截所有請求
public class LogFilter implements Filter {

  @Override
  public void init(FilterConfig config) {
    String appName = config.getInitParameter("appName");
    System.out.println("[" + LocalDateTime.now() + "] " + appName + " Filter 初始化完成");
  }

  @Override
  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
      throws IOException, ServletException {
    
    HttpServletRequest request = (HttpServletRequest) req;
    long start = System.currentTimeMillis();
    
    // 前置處理
    System.out.println(">>> 請求: " + request.getMethod() + " " + request.getRequestURI()
        + " 來自 " + request.getRemoteAddr());
    
    try {
      chain.doFilter(req, resp); // 傳遞給下一個 Filter 或目標 Servlet
    } finally {
      // 後置處理 (確保執行)
      long duration = System.currentTimeMillis() - start;
      System.out.println("<<< 完成: " + request.getRequestURI() + " 耗時 " + duration + "ms");
    }
  }

  @Override
  public void destroy() {
    System.out.println("LogFilter 銷毀");
  }
}

web.xml 宣告與映射 (替代 @WebFilter)

<filter>
  <filter-name>LogFilter</filter-name>
  <filter-class>com.example.filter.LogFilter</filter-class>
  <init-param>
    <param-name>appName</param-name>
    <param-value>MyWebApp</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>LogFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 也可映射到特定 Servlet -->
<filter-mapping>
  <filter-name>LogFilter</filter-name>
  <servlet-name>MyServlet</servlet-name>
</filter-mapping>

Dispatcher 類型控制

<filter-mapping>
  <filter-name>LogFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>      <!-- 直接請求 (預設) -->
  <dispatcher>FORWARD</dispatcher>     <!-- RequestDispatcher.forward() -->
  <dispatcher>INCLUDE</dispatcher>     <!-- RequestDispatcher.include() -->
  <dispatcher>ERROR</dispatcher>       <!-- 錯誤頁面 -->
  <dispatcher>ASYNC</dispatcher>       <!-- 非同步請求 -->
</filter-mapping>

多重 Filter 執行順序

順序由 web.xml 中 <filter-mapping> 出現順序決定,或 @WebFilter 無保證順序 (需 @Order 或 web.xml 指定)。

<!-- 先執行 EncodingFilter,再執行 AuthFilter -->
<filter-mapping>
  <filter-name>EncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>AuthFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

常見應用場景與範例

1. 字元編碼過濾器 (解決中文亂碼)

public class EncodingFilter implements Filter {
  private String encoding = "UTF-8";
  @Override public void init(FilterConfig cfg) {
    String e = cfg.getInitParameter("encoding");
    if (e != null) encoding = e;
  }
  @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
      throws IOException, ServletException {
    req.setCharacterEncoding(encoding);
    resp.setCharacterEncoding(encoding);
    resp.setContentType("text/html;charset=" + encoding);
    chain.doFilter(req, resp);
  }
}

2. 登入驗證過濾器 (權限控制)

@WebFilter("/member/*")
public class AuthFilter implements Filter {
  @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest r = (HttpServletRequest) req;
    HttpServletResponse s = (HttpServletResponse) resp;
    if (r.getSession().getAttribute("loginUser") == null) {
      s.sendRedirect(r.getContextPath() + "/login.jsp?redirect=" + r.getRequestURI());
      return;
    }
    chain.doFilter(req, resp);
  }
}

3. XSS 防護包裝請求

public class XssFilter implements Filter {
  @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
    chain.doFilter(new XssRequestWrapper((HttpServletRequest) req), resp);
  }
}

// 包裝類覆寫 getParameter 等方法,對輸入進行 HTML Escape
class XssRequestWrapper extends HttpServletRequestWrapper {
  // ... 實作細節略
}

4. GZIP 壓縮回應

public class GzipFilter implements Filter {
  @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest r = (HttpServletRequest) req;
    HttpServletResponse s = (HttpServletResponse) resp;
    if (r.getHeader("Accept-Encoding") != null && r.getHeader("Accept-Encoding").contains("gzip")) {
      GZIPResponseWrapper wrapped = new GZIPResponseWrapper(s);
      chain.doFilter(req, wrapped);
      wrapped.finish(); // 確保壓縮資料寫出
    } else {
      chain.doFilter(req, resp);
    }
  }
}

FilterConfig 取得初始化參數

public void init(FilterConfig config) {
  // 單一參數
  String encoding = config.getInitParameter("encoding");
  // 所有參數
  Enumeration names = config.getInitParameterNames();
  while (names.hasMoreElements()) {
    String name = names.nextElement();
    System.out.println(name + " = " + config.getInitParameter(name));
  }
  // 取得 ServletContext
  ServletContext ctx = config.getServletContext();
}

最佳實踐與注意事項

  1. 務必呼叫 chain.doFilter(),否則請求鏈中斷,目標資源不會執行。
  2. try-finally 包裝後置處理,確保即使拋出例外也能執行清理/記錄。
  3. 不要在 Filter 中處理業務邏輯,專注橫切關注點 (Cross-cutting Concerns)。
  4. 非同步請求支援:Servlet 3.0+ 需在 @WebFilter(asyncSupported = true) 並正確處理 AsyncContext。
  5. 效能考量:避免在 doFilter 中做重 I/O、資料庫查詢;若需共享資料,使用 request.setAttribute()。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved