JavaWeb?中的?Filter組件詳解
JavaWeb 中的 Filter 詳解
1. Filter 基本概念
1.1 什么是 Filter
Filter(過濾器)是 JavaWeb 中的一種組件,用于在請求到達 Servlet 之前或響應(yīng)發(fā)送到客戶端之前對請求和響應(yīng)進行預(yù)處理和后處理。
1.2 Filter 的作用
- 請求預(yù)處理:在請求到達 Servlet 之前進行處理
- 響應(yīng)后處理:在響應(yīng)發(fā)送到客戶端之前進行處理
- 鏈式處理:多個 Filter 可以形成處理鏈
- 動態(tài)攔截:根據(jù)條件決定是否繼續(xù)執(zhí)行
2. Filter 的工作原理
2.1 Filter 生命周期
public class MyFilter implements Filter {
// 1. 初始化方法 - 容器啟動時調(diào)用
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter 初始化");
}
// 2. 過濾方法 - 每次請求時調(diào)用
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 前置處理
System.out.println("請求到達 Filter");
// 傳遞給下一個 Filter 或 Servlet
chain.doFilter(request, response);
// 后置處理
System.out.println("響應(yīng)經(jīng)過 Filter");
}
// 3. 銷毀方法 - 容器關(guān)閉時調(diào)用
@Override
public void destroy() {
System.out.println("Filter 銷毀");
}
}2.2 Filter 執(zhí)行流程
客戶端請求 → 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 實現(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 實現(xiàn)示例
5.1 字符編碼過濾器
@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è)置請求編碼
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 身份認證過濾器
@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) {
// 未登錄,重定向到登錄頁面
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() + " 訪問: " + requestURI);
chain.doFilter(request, response);
}
}5.3 日志記錄過濾器
@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();
// 記錄請求信息
String clientIP = getClientIP(httpRequest);
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
logger.info("請求開始: {} {} 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("請求完成: {} {} - 狀態(tài): {} - 耗時: {}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 跨域過濾器 (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ù)檢請求
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}5.5 XSS 防護過濾器
@WebFilter("/*")
public class XSSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 包裝請求對象,對參數(shù)進行 XSS 過濾
XSSRequestWrapper wrappedRequest = new XSSRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
}
// XSS 請求包裝器
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;
// 簡單的 XSS 過濾
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 路徑下的所有請求
"/admin/*", // /admin 路徑下的所有請求
"/*" // 所有請求
}
)6.2 Dispatcher 類型
@WebFilter(
urlPatterns = "/*",
dispatcherTypes = {
DispatcherType.REQUEST, // 直接請求
DispatcherType.FORWARD, // 轉(zhuǎn)發(fā)請求
DispatcherType.INCLUDE, // 包含請求
DispatcherType.ERROR, // 錯誤處理
DispatcherType.ASYNC // 異步請求
}
)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 的高級用法
8.1 條件過濾
@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;
}
// 對動態(tài)請求進行特殊處理
System.out.println("處理動態(tài)請求: " + requestURI);
// 添加自定義請求屬性
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("請求處理耗時: " + 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 的最佳實踐
9.1 性能考慮
@WebFilter("/*")
public class PerformanceFilter implements Filter {
private boolean enableProfiling;
@Override
public void init(FilterConfig filterConfig) {
// 從配置中讀取性能開關(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) { // 超過1秒的記錄警告
System.err.println("慢請求: " + 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("處理請求時發(fā)生異常: " + httpRequest.getRequestURI());
e.printStackTrace();
// 返回統(tǒng)一的錯誤響應(yīng)
httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
httpResponse.setContentType("application/json");
String errorJson = "{\"error\": \"服務(wù)器內(nèi)部錯誤\", \"code\": 500}";
httpResponse.getWriter().write(errorJson);
}
}10. Filter 的測試
10.1 單元測試示例
public class AuthenticationFilterTest {
@Test
public void testDoFilter_AuthenticatedUser() throws Exception {
// 創(chuàng)建模擬對象
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í)行測試
AuthenticationFilter filter = new AuthenticationFilter();
filter.doFilter(mockRequest, mockResponse, mockChain);
// 驗證結(jié)果
verify(mockChain).doFilter(mockRequest, mockResponse);
}
@Test
public void testDoFilter_UnauthenticatedUser() throws Exception {
// 創(chuàng)建模擬對象
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í)行測試
AuthenticationFilter filter = new AuthenticationFilter();
filter.doFilter(mockRequest, mockResponse, mockChain);
// 驗證重定向
verify(mockResponse).sendRedirect("/myapp/login");
verify(mockChain, never()).doFilter(mockRequest, mockResponse);
}
}總結(jié)
Filter 是 JavaWeb 中非常重要的組件,它提供了以下核心功能:
- 請求預(yù)處理和響應(yīng)后處理
- 鏈式處理機制
- 靈活的配置方式
- 強大的攔截能力
通過合理使用 Filter,可以實現(xiàn):
- 統(tǒng)一的字符編碼處理
- 身份認證和授權(quán)
- 請求日志記錄
- 性能監(jiān)控
- 安全防護
- 跨域處理
- 數(shù)據(jù)壓縮等
Filter 的設(shè)計遵循了 AOP(面向切面編程)的思想,使得橫切關(guān)注點能夠與業(yè)務(wù)邏輯分離,提高了代碼的可維護性和復(fù)用性。
到此這篇關(guān)于JavaWeb 中的 Filter 詳解的文章就介紹到這了,更多相關(guān)javaweb filter內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot?AOP中JoinPoint的使用方式和通知切點表達式
這篇文章主要介紹了SpringBoot?AOP中JoinPoint的使用方式和通知切點表達式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
springboot2如何集成ElasticSearch6.4.3
這篇文章主要介紹了springboot2如何集成ElasticSearch6.4.3問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
VS?Code中運行Java?SpringBoot的項目詳細步驟
這篇文章主要介紹了VS?Code中運行Java?SpringBoot項目的相關(guān)資料,文中涵蓋了安裝必要的擴展、配置環(huán)境、創(chuàng)建或?qū)腠椖俊⑴渲谜{(diào)試環(huán)境、運行和調(diào)試項目、使用Spring?Boot?Actuator以及配置任務(wù)自動化等步驟,需要的朋友可以參考下2024-12-12
SpringSecurity實現(xiàn)訪問控制url匹配
本文主要介紹了SpringSecurity實現(xiàn)訪問控制url匹配,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
Java集合框架實戰(zhàn)HashMap與HashSet的使用案例
本文通過兩個案例詳細介紹了HashMap和HashSet在Java中的應(yīng)用,通過這些案例,我們學(xué)習(xí)了如何選擇合適的集合類型、重寫equals和hashCode方法、利用泛型和并發(fā)集合等最佳實踐,感興趣的朋友跟隨小編一起看看吧2025-11-11

