JavaWeb?中的?Filter組件詳解
JavaWeb 中的 Filter 詳解
1. Filter 基本概念
1.1 什么是 Filter
Filter(過(guò)濾器)是 JavaWeb 中的一種組件,用于在請(qǐng)求到達(dá) Servlet 之前或響應(yīng)發(fā)送到客戶端之前對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理和后處理。
1.2 Filter 的作用
- 請(qǐng)求預(yù)處理:在請(qǐng)求到達(dá) Servlet 之前進(jìn)行處理
- 響應(yīng)后處理:在響應(yīng)發(fā)送到客戶端之前進(jìn)行處理
- 鏈?zhǔn)教幚?/strong>:多個(gè) Filter 可以形成處理鏈
- 動(dòng)態(tài)攔截:根據(jù)條件決定是否繼續(xù)執(zhí)行
2. Filter 的工作原理
2.1 Filter 生命周期
public class MyFilter implements Filter {
// 1. 初始化方法 - 容器啟動(dòng)時(shí)調(diào)用
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter 初始化");
}
// 2. 過(guò)濾方法 - 每次請(qǐng)求時(shí)調(diào)用
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 前置處理
System.out.println("請(qǐng)求到達(dá) Filter");
// 傳遞給下一個(gè) Filter 或 Servlet
chain.doFilter(request, response);
// 后置處理
System.out.println("響應(yīng)經(jīng)過(guò) Filter");
}
// 3. 銷毀方法 - 容器關(guān)閉時(shí)調(diào)用
@Override
public void destroy() {
System.out.println("Filter 銷毀");
}
}2.2 Filter 執(zhí)行流程
客戶端請(qǐng)求 → Filter1 → Filter2 → ... → Servlet → Filter2 → Filter1 → 客戶端響應(yīng)
3. Filter 的核心接口和類
3.1 Filter 接口
public interface Filter {
void init(FilterConfig filterConfig);
void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain);
void destroy();
}
3.2 FilterConfig 接口
public interface FilterConfig {
String getFilterName();
ServletContext getServletContext();
String getInitParameter(String name);
Enumeration<String> getInitParameterNames();
}
3.3 FilterChain 接口
public interface FilterChain {
void doFilter(ServletRequest request, ServletResponse response);
}
4. Filter 的配置方式
4.1 注解配置(推薦)
@WebFilter(
filterName = "myFilter",
urlPatterns = {"/*"},
initParams = {
@WebInitParam(name = "encoding", value = "UTF-8"),
@WebInitParam(name = "excludePaths", value = "/static/*")
}
)
public class MyFilter implements Filter {
// Filter 實(shí)現(xiàn)
}
4.2 web.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
version="3.1">
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
</web-app>5. 常用 Filter 實(shí)現(xiàn)示例
5.1 字符編碼過(guò)濾器
@WebFilter(
urlPatterns = "/*",
initParams = @WebInitParam(name = "encoding", value = "UTF-8")
)
public class CharacterEncodingFilter implements Filter {
private String encoding = "UTF-8";
@Override
public void init(FilterConfig filterConfig) {
String encodingParam = filterConfig.getInitParameter("encoding");
if (encodingParam != null && !encodingParam.isEmpty()) {
this.encoding = encodingParam;
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 設(shè)置請(qǐng)求編碼
request.setCharacterEncoding(encoding);
// 設(shè)置響應(yīng)編碼
response.setCharacterEncoding(encoding);
response.setContentType("text/html;charset=" + encoding);
System.out.println("設(shè)置字符編碼: " + encoding);
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 清理資源
}
}5.2 身份認(rèn)證過(guò)濾器
@WebFilter(urlPatterns = {"/admin/*", "/user/*"})
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession session = httpRequest.getSession(false);
// 檢查用戶是否登錄
if (session == null || session.getAttribute("user") == null) {
// 未登錄,重定向到登錄頁(yè)面
httpResponse.sendRedirect(httpRequest.getContextPath() + "/login");
return;
}
// 檢查用戶權(quán)限
User user = (User) session.getAttribute("user");
String requestURI = httpRequest.getRequestURI();
if (requestURI.contains("/admin/") && !user.isAdmin()) {
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "權(quán)限不足");
return;
}
System.out.println("用戶 " + user.getUsername() + " 訪問(wèn): " + requestURI);
chain.doFilter(request, response);
}
}5.3 日志記錄過(guò)濾器
@WebFilter("/*")
public class LoggingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
long startTime = System.currentTimeMillis();
// 記錄請(qǐng)求信息
String clientIP = getClientIP(httpRequest);
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
logger.info("請(qǐng)求開(kāi)始: {} {} from {}", method, requestURI, clientIP);
try {
chain.doFilter(request, response);
} finally {
// 記錄響應(yīng)信息
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
HttpServletResponse httpResponse = (HttpServletResponse) response;
int status = httpResponse.getStatus();
logger.info("請(qǐng)求完成: {} {} - 狀態(tài): {} - 耗時(shí): {}ms",
method, requestURI, status, duration);
}
}
private String getClientIP(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0];
}
return request.getRemoteAddr();
}
}5.4 跨域過(guò)濾器 (CORS)
@WebFilter("/*")
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 設(shè)置 CORS 頭
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers",
"Content-Type, Authorization, X-Requested-With");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
// 處理預(yù)檢請(qǐng)求
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}5.5 XSS 防護(hù)過(guò)濾器
@WebFilter("/*")
public class XSSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 包裝請(qǐng)求對(duì)象,對(duì)參數(shù)進(jìn)行 XSS 過(guò)濾
XSSRequestWrapper wrappedRequest = new XSSRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
}
// XSS 請(qǐng)求包裝器
class XSSRequestWrapper extends HttpServletRequestWrapper {
public XSSRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return cleanXSS(value);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) return null;
String[] cleanedValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
cleanedValues[i] = cleanXSS(values[i]);
}
return cleanedValues;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
return cleanXSS(value);
}
private String cleanXSS(String value) {
if (value == null) return null;
// 簡(jiǎn)單的 XSS 過(guò)濾
return value.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """)
.replaceAll("'", "'")
.replaceAll("/", "/")
.replaceAll("\\(", "(")
.replaceAll("\\)", ")")
.replaceAll("script", "scr_ipt")
.replaceAll("javascript", "java_script");
}
}6. Filter 的配置參數(shù)詳解
6.1 URL 模式匹配
@WebFilter(
urlPatterns = {
"*.html", // 所有 HTML 文件
"/api/*", // /api 路徑下的所有請(qǐng)求
"/admin/*", // /admin 路徑下的所有請(qǐng)求
"/*" // 所有請(qǐng)求
}
)6.2 Dispatcher 類型
@WebFilter(
urlPatterns = "/*",
dispatcherTypes = {
DispatcherType.REQUEST, // 直接請(qǐng)求
DispatcherType.FORWARD, // 轉(zhuǎn)發(fā)請(qǐng)求
DispatcherType.INCLUDE, // 包含請(qǐng)求
DispatcherType.ERROR, // 錯(cuò)誤處理
DispatcherType.ASYNC // 異步請(qǐng)求
}
)6.3 初始化參數(shù)
@WebFilter(
urlPatterns = "/*",
initParams = {
@WebInitParam(name = "excludePaths", value = "/static/,/public/"),
@WebInitParam(name = "maxFileSize", value = "10485760"), // 10MB
@WebInitParam(name = "allowedTypes", value = "jpg,png,pdf")
}
)
7. Filter 的執(zhí)行順序
7.1 執(zhí)行順序規(guī)則
- web.xml 配置順序:按照配置文件中定義的順序執(zhí)行
- 注解配置順序:使用
@WebFilter的filterName字母順序 - 混合配置:web.xml 優(yōu)先于注解
7.2 控制執(zhí)行順序的方法
// 方法1: 使用 web.xml 明確指定順序
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
// 方法2: 使用 @Order 注解(需要框架支持)
@Order(1)
@WebFilter("/*")
public class FirstFilter implements Filter { }
@Order(2)
@WebFilter("/*")
public class SecondFilter implements Filter { }8. Filter 的高級(jí)用法
8.1 條件過(guò)濾
@WebFilter("/*")
public class ConditionalFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
// 排除靜態(tài)資源
if (requestURI.startsWith("/static/") ||
requestURI.endsWith(".css") ||
requestURI.endsWith(".js")) {
chain.doFilter(request, response);
return;
}
// 對(duì)動(dòng)態(tài)請(qǐng)求進(jìn)行特殊處理
System.out.println("處理動(dòng)態(tài)請(qǐng)求: " + requestURI);
// 添加自定義請(qǐng)求屬性
httpRequest.setAttribute("processingTime", System.currentTimeMillis());
chain.doFilter(request, response);
// 后處理
Long startTime = (Long) httpRequest.getAttribute("processingTime");
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
System.out.println("請(qǐng)求處理耗時(shí): " + duration + "ms");
}
}
}8.2 響應(yīng)包裝器
@WebFilter("/*")
public class CompressionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
// 使用 GZIP 壓縮響應(yīng)
CompressionResponseWrapper wrappedResponse =
new CompressionResponseWrapper(httpResponse);
chain.doFilter(request, wrappedResponse);
// 完成壓縮并發(fā)送響應(yīng)
wrappedResponse.finish();
} else {
chain.doFilter(request, response);
}
}
}
// 壓縮響應(yīng)包裝器
class CompressionResponseWrapper extends HttpServletResponseWrapper {
private GZIPServletOutputStream gzipStream;
private PrintWriter printWriter;
public CompressionResponseWrapper(HttpServletResponse response)
throws IOException {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (printWriter != null) {
throw new IllegalStateException("getWriter() has already been called");
}
if (gzipStream == null) {
gzipStream = new GZIPServletOutputStream(super.getOutputStream());
}
return gzipStream;
}
@Override
public PrintWriter getWriter() throws IOException {
if (gzipStream != null) {
throw new IllegalStateException("getOutputStream() has already been called");
}
if (printWriter == null) {
gzipStream = new GZIPServletOutputStream(super.getOutputStream());
printWriter = new PrintWriter(new OutputStreamWriter(gzipStream,
getCharacterEncoding()));
}
return printWriter;
}
public void finish() throws IOException {
if (printWriter != null) {
printWriter.close();
}
if (gzipStream != null) {
gzipStream.finish();
}
}
}9. Filter 的最佳實(shí)踐
9.1 性能考慮
@WebFilter("/*")
public class PerformanceFilter implements Filter {
private boolean enableProfiling;
@Override
public void init(FilterConfig filterConfig) {
// 從配置中讀取性能開(kāi)關(guān)
String profiling = filterConfig.getInitParameter("enableProfiling");
this.enableProfiling = "true".equalsIgnoreCase(profiling);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!enableProfiling) {
chain.doFilter(request, response);
return;
}
long startTime = System.nanoTime();
try {
chain.doFilter(request, response);
} finally {
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1_000_000; // 轉(zhuǎn)換為毫秒
if (duration > 1000) { // 超過(guò)1秒的記錄警告
System.err.println("慢請(qǐng)求: " + duration + "ms");
}
}
}
}9.2 異常處理
@WebFilter("/*")
public class ExceptionHandlingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
// 統(tǒng)一異常處理
handleException(e, request, response);
}
}
private void handleException(Exception e, ServletRequest request,
ServletResponse response) throws IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 記錄異常日志
System.err.println("處理請(qǐng)求時(shí)發(fā)生異常: " + httpRequest.getRequestURI());
e.printStackTrace();
// 返回統(tǒng)一的錯(cuò)誤響應(yīng)
httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
httpResponse.setContentType("application/json");
String errorJson = "{\"error\": \"服務(wù)器內(nèi)部錯(cuò)誤\", \"code\": 500}";
httpResponse.getWriter().write(errorJson);
}
}10. Filter 的測(cè)試
10.1 單元測(cè)試示例
public class AuthenticationFilterTest {
@Test
public void testDoFilter_AuthenticatedUser() throws Exception {
// 創(chuàng)建模擬對(duì)象
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
FilterChain mockChain = mock(FilterChain.class);
HttpSession mockSession = mock(HttpSession.class);
// 設(shè)置模擬行為
when(mockRequest.getSession(false)).thenReturn(mockSession);
when(mockSession.getAttribute("user")).thenReturn(new User("testUser"));
when(mockRequest.getRequestURI()).thenReturn("/user/profile");
// 執(zhí)行測(cè)試
AuthenticationFilter filter = new AuthenticationFilter();
filter.doFilter(mockRequest, mockResponse, mockChain);
// 驗(yàn)證結(jié)果
verify(mockChain).doFilter(mockRequest, mockResponse);
}
@Test
public void testDoFilter_UnauthenticatedUser() throws Exception {
// 創(chuàng)建模擬對(duì)象
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
FilterChain mockChain = mock(FilterChain.class);
// 設(shè)置模擬行為
when(mockRequest.getSession(false)).thenReturn(null);
when(mockRequest.getContextPath()).thenReturn("/myapp");
// 執(zhí)行測(cè)試
AuthenticationFilter filter = new AuthenticationFilter();
filter.doFilter(mockRequest, mockResponse, mockChain);
// 驗(yàn)證重定向
verify(mockResponse).sendRedirect("/myapp/login");
verify(mockChain, never()).doFilter(mockRequest, mockResponse);
}
}總結(jié)
Filter 是 JavaWeb 中非常重要的組件,它提供了以下核心功能:
- 請(qǐng)求預(yù)處理和響應(yīng)后處理
- 鏈?zhǔn)教幚?/strong>機(jī)制
- 靈活的配置方式
- 強(qiáng)大的攔截能力
通過(guò)合理使用 Filter,可以實(shí)現(xiàn):
- 統(tǒng)一的字符編碼處理
- 身份認(rèn)證和授權(quán)
- 請(qǐng)求日志記錄
- 性能監(jiān)控
- 安全防護(hù)
- 跨域處理
- 數(shù)據(jù)壓縮等
Filter 的設(shè)計(jì)遵循了 AOP(面向切面編程)的思想,使得橫切關(guān)注點(diǎn)能夠與業(yè)務(wù)邏輯分離,提高了代碼的可維護(hù)性和復(fù)用性。
到此這篇關(guān)于JavaWeb 中的 Filter 詳解的文章就介紹到這了,更多相關(guān)javaweb filter內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot?AOP中JoinPoint的使用方式和通知切點(diǎn)表達(dá)式
這篇文章主要介紹了SpringBoot?AOP中JoinPoint的使用方式和通知切點(diǎn)表達(dá)式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
springboot2如何集成ElasticSearch6.4.3
這篇文章主要介紹了springboot2如何集成ElasticSearch6.4.3問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
Java實(shí)現(xiàn)PDF文檔自動(dòng)化比較的教程解析
在日常工作中,我們經(jīng)常需要處理大量的 PDF 文檔,本文將深入探討如何利用 Java 技術(shù),結(jié)合強(qiáng)大的 Spire.PDF for Java 庫(kù),實(shí)現(xiàn) PDF 文檔的自動(dòng)化比較,感興趣的可以了解下2025-11-11
VS?Code中運(yùn)行Java?SpringBoot的項(xiàng)目詳細(xì)步驟
這篇文章主要介紹了VS?Code中運(yùn)行Java?SpringBoot項(xiàng)目的相關(guān)資料,文中涵蓋了安裝必要的擴(kuò)展、配置環(huán)境、創(chuàng)建或?qū)腠?xiàng)目、配置調(diào)試環(huán)境、運(yùn)行和調(diào)試項(xiàng)目、使用Spring?Boot?Actuator以及配置任務(wù)自動(dòng)化等步驟,需要的朋友可以參考下2024-12-12
Java詳解entity轉(zhuǎn)換到vo過(guò)程
這篇文章將用實(shí)例來(lái)和大家介紹一下entity轉(zhuǎn)換到vo的方法過(guò)程。文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下2022-06-06
SpringBoot實(shí)現(xiàn)熱啟動(dòng)的三種方案
在SpringBoot項(xiàng)目中,傳統(tǒng)開(kāi)發(fā)模式下每次修改代碼都需要手動(dòng)重啟應(yīng)用,這會(huì)觸發(fā)完整的 JVM 關(guān)閉與重新加載過(guò)程,對(duì)于大型項(xiàng)目,這個(gè)流程耗時(shí) 10–30 秒,所以本文給大家介紹了SpringBoot實(shí)現(xiàn)熱啟動(dòng)的三種方案,需要的朋友可以參考下2025-11-11
SpringSecurity實(shí)現(xiàn)訪問(wèn)控制url匹配
本文主要介紹了SpringSecurity實(shí)現(xiàn)訪問(wèn)控制url匹配,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
Java集合框架實(shí)戰(zhàn)HashMap與HashSet的使用案例
本文通過(guò)兩個(gè)案例詳細(xì)介紹了HashMap和HashSet在Java中的應(yīng)用,通過(guò)這些案例,我們學(xué)習(xí)了如何選擇合適的集合類型、重寫(xiě)equals和hashCode方法、利用泛型和并發(fā)集合等最佳實(shí)踐,感興趣的朋友跟隨小編一起看看吧2025-11-11

