java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > javaweb filter

JavaWeb 中的 Filter组件详解

作者:总会落叶

本文详细介绍了JavaWeb中的Filter组件,包括其基本概念、工作原理、核心接口和类、配置方式以及常见应用示例,Filter可以实现请求预处理、响应后处理、链式处理等功能,通过配置文件或注解进行设置,适用于字符编码、身份认证、日志记录等多种场景,感兴趣的朋友一起学习吧

JavaWeb 中的 Filter 详解

1. Filter 基本概念

1.1 什么是 Filter

Filter(过滤器)是 JavaWeb 中的一种组件,用于在请求到达 Servlet 之前或响应发送到客户端之前对请求和响应进行预处理和后处理。

1.2 Filter 的作用

2. Filter 的工作原理

2.1 Filter 生命周期

public class MyFilter implements Filter {
    // 1. 初始化方法 - 容器启动时调用
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter 初始化");
    }
    // 2. 过滤方法 - 每次请求时调用
    @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("响应经过 Filter");
    }
    // 3. 销毁方法 - 容器关闭时调用
    @Override
    public void destroy() {
        System.out.println("Filter 销毁");
    }
}

2.2 Filter 执行流程

客户端请求 → Filter1 → Filter2 → ... → Servlet → Filter2 → Filter1 → 客户端响应

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 实现
}

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 实现示例

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 {
        // 设置请求编码
        request.setCharacterEncoding(encoding);
        // 设置响应编码
        response.setCharacterEncoding(encoding);
        response.setContentType("text/html;charset=" + encoding);
        System.out.println("设置字符编码: " + 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;
        }
        // 检查用户权限
        User user = (User) session.getAttribute("user");
        String requestURI = httpRequest.getRequestURI();
        if (requestURI.contains("/admin/") && !user.isAdmin()) {
            httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "权限不足");
            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 {
            // 记录响应信息
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            int status = httpResponse.getStatus();
            logger.info("请求完成: {} {} - 状态: {} - 耗时: {}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;
        // 设置 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");
        // 处理预检请求
        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 {
        // 包装请求对象,对参数进行 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("\"", "&quot;")
                   .replaceAll("'", "&#x27;")
                   .replaceAll("/", "&#x2F;")
                   .replaceAll("\\(", "&#40;")
                   .replaceAll("\\)", "&#41;")
                   .replaceAll("script", "scr_ipt")
                   .replaceAll("javascript", "java_script");
    }
}

6. Filter 的配置参数详解

6.1 URL 模式匹配

@WebFilter(
    urlPatterns = {
        "*.html",           // 所有 HTML 文件
        "/api/*",           // /api 路径下的所有请求
        "/admin/*",         // /admin 路径下的所有请求
        "/*"                // 所有请求
    }
)

6.2 Dispatcher 类型

@WebFilter(
    urlPatterns = "/*",
    dispatcherTypes = {
        DispatcherType.REQUEST,     // 直接请求
        DispatcherType.FORWARD,     // 转发请求
        DispatcherType.INCLUDE,     // 包含请求
        DispatcherType.ERROR,       // 错误处理
        DispatcherType.ASYNC        // 异步请求
    }
)

6.3 初始化参数

@WebFilter(
    urlPatterns = "/*",
    initParams = {
        @WebInitParam(name = "excludePaths", value = "/static/,/public/"),
        @WebInitParam(name = "maxFileSize", value = "10485760"), // 10MB
        @WebInitParam(name = "allowedTypes", value = "jpg,png,pdf")
    }
)

7. Filter 的执行顺序

7.1 执行顺序规则

  1. web.xml 配置顺序:按照配置文件中定义的顺序执行
  2. 注解配置顺序:使用 @WebFilterfilterName 字母顺序
  3. 混合配置:web.xml 优先于注解

7.2 控制执行顺序的方法

// 方法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();
        // 排除静态资源
        if (requestURI.startsWith("/static/") || 
            requestURI.endsWith(".css") || 
            requestURI.endsWith(".js")) {
            chain.doFilter(request, response);
            return;
        }
        // 对动态请求进行特殊处理
        System.out.println("处理动态请求: " + 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 响应包装器

@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 压缩响应
            CompressionResponseWrapper wrappedResponse = 
                new CompressionResponseWrapper(httpResponse);
            chain.doFilter(request, wrappedResponse);
            // 完成压缩并发送响应
            wrappedResponse.finish();
        } else {
            chain.doFilter(request, response);
        }
    }
}
// 压缩响应包装器
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) {
        // 从配置中读取性能开关
        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; // 转换为毫秒
            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) {
            // 统一异常处理
            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("处理请求时发生异常: " + httpRequest.getRequestURI());
        e.printStackTrace();
        // 返回统一的错误响应
        httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        httpResponse.setContentType("application/json");
        String errorJson = "{\"error\": \"服务器内部错误\", \"code\": 500}";
        httpResponse.getWriter().write(errorJson);
    }
}

10. Filter 的测试

10.1 单元测试示例

public class AuthenticationFilterTest {
    @Test
    public void testDoFilter_AuthenticatedUser() throws Exception {
        // 创建模拟对象
        HttpServletRequest mockRequest = mock(HttpServletRequest.class);
        HttpServletResponse mockResponse = mock(HttpServletResponse.class);
        FilterChain mockChain = mock(FilterChain.class);
        HttpSession mockSession = mock(HttpSession.class);
        // 设置模拟行为
        when(mockRequest.getSession(false)).thenReturn(mockSession);
        when(mockSession.getAttribute("user")).thenReturn(new User("testUser"));
        when(mockRequest.getRequestURI()).thenReturn("/user/profile");
        // 执行测试
        AuthenticationFilter filter = new AuthenticationFilter();
        filter.doFilter(mockRequest, mockResponse, mockChain);
        // 验证结果
        verify(mockChain).doFilter(mockRequest, mockResponse);
    }
    @Test
    public void testDoFilter_UnauthenticatedUser() throws Exception {
        // 创建模拟对象
        HttpServletRequest mockRequest = mock(HttpServletRequest.class);
        HttpServletResponse mockResponse = mock(HttpServletResponse.class);
        FilterChain mockChain = mock(FilterChain.class);
        // 设置模拟行为
        when(mockRequest.getSession(false)).thenReturn(null);
        when(mockRequest.getContextPath()).thenReturn("/myapp");
        // 执行测试
        AuthenticationFilter filter = new AuthenticationFilter();
        filter.doFilter(mockRequest, mockResponse, mockChain);
        // 验证重定向
        verify(mockResponse).sendRedirect("/myapp/login");
        verify(mockChain, never()).doFilter(mockRequest, mockResponse);
    }
}

总结

Filter 是 JavaWeb 中非常重要的组件,它提供了以下核心功能:

  1. 请求预处理响应后处理
  2. 链式处理机制
  3. 灵活的配置方式
  4. 强大的拦截能力

通过合理使用 Filter,可以实现:

Filter 的设计遵循了 AOP(面向切面编程)的思想,使得横切关注点能够与业务逻辑分离,提高了代码的可维护性和复用性。

到此这篇关于JavaWeb 中的 Filter 详解的文章就介绍到这了,更多相关javaweb filter内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文