Java Servlet过滤器案例如何开发

wen java案例 25

本文目录导读:

Java Servlet过滤器案例如何开发

  1. Servlet过滤器基础
  2. 完整开发案例
  3. 配置方式
  4. 过滤器链执行顺序
  5. Maven依赖配置
  6. 最佳实践建议
  7. 常见问题排查

我来详细介绍Java Servlet过滤器的开发案例,从基础概念到完整实现。

Servlet过滤器基础

什么是Servlet过滤器

过滤器是Java Web组件,可以在请求到达Servlet之前或响应返回客户端之前进行预处理和后处理。

过滤器的典型应用场景

  • 用户认证和授权
  • 请求日志记录
  • 数据压缩
  • 输入验证
  • 字符编码设置
  • 敏感信息过滤

完整开发案例

案例1:字符编码过滤器

package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 字符编码过滤器 - 统一设置请求和响应的字符编码
 */
@WebFilter(urlPatterns = "/*")  // 过滤所有请求
public class CharacterEncodingFilter implements Filter {
    private String encoding = "UTF-8";
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 从web.xml或注解获取初始化参数
        String encodingParam = filterConfig.getInitParameter("encoding");
        if (encodingParam != null) {
            encoding = encodingParam;
        }
        System.out.println("字符编码过滤器初始化完成,编码:" + encoding);
    }
    @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);
        // 继续执行后续过滤器或目标Servlet
        chain.doFilter(request, response);
        System.out.println("响应已处理完成");
    }
    @Override
    public void destroy() {
        System.out.println("字符编码过滤器被销毁");
    }
}

案例2:用户登录认证过滤器

package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
 * 用户登录认证过滤器 - 检查用户是否已登录
 */
@WebFilter(urlPatterns = {"/admin/*", "/user/*"})  // 只过滤管理后台和用户相关页面
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("登录认证过滤器初始化");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 获取当前请求的URI
        String requestURI = httpRequest.getRequestURI();
        String contextPath = httpRequest.getContextPath();
        String requestPath = requestURI.substring(contextPath.length());
        // 允许访问的公共路径(不需要登录)
        if (requestPath.equals("/login") || requestPath.equals("/register") 
            || requestPath.equals("/index")) {
            chain.doFilter(request, response);
            return;
        }
        // 检查用户是否已登录
        HttpSession session = httpRequest.getSession(false);
        boolean isLoggedIn = (session != null && session.getAttribute("user") != null);
        if (isLoggedIn) {
            // 用户已登录,继续执行
            System.out.println("用户已认证,允许访问:" + requestPath);
            chain.doFilter(request, response);
        } else {
            // 用户未登录,重定向到登录页面
            System.out.println("用户未登录,重定向到登录页面");
            httpResponse.sendRedirect(contextPath + "/login");
        }
    }
    @Override
    public void destroy() {
        System.out.println("登录认证过滤器被销毁");
    }
}

案例3:请求日志记录过滤器

package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * 请求日志记录过滤器 - 记录所有请求信息
 */
@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("日志过滤器初始化");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        // 收集请求信息
        String ip = httpRequest.getRemoteAddr();
        String method = httpRequest.getMethod();
        String uri = httpRequest.getRequestURI();
        String queryString = httpRequest.getQueryString();
        String userAgent = httpRequest.getHeader("User-Agent");
        String sessionId = httpRequest.getSession().getId();
        String timestamp = dateFormat.format(new Date());
        // 记录请求开始时间
        long startTime = System.currentTimeMillis();
        // 构建日志信息
        StringBuilder logInfo = new StringBuilder();
        logInfo.append("[").append(timestamp).append("] ");
        logInfo.append("IP: ").append(ip).append(" ");
        logInfo.append("Session: ").append(sessionId).append(" ");
        logInfo.append("Method: ").append(method).append(" ");
        logInfo.append("URI: ").append(uri);
        if (queryString != null) {
            logInfo.append("?").append(queryString);
        }
        logInfo.append(" ").append(userAgent);
        // 打印日志
        System.out.println("请求开始: " + logInfo.toString());
        // 使用包装类记录响应状态和耗时
        ResponseWrapper responseWrapper = new ResponseWrapper(httpRequest, response);
        try {
            // 继续执行
            chain.doFilter(request, responseWrapper);
        } finally {
            // 计算耗时
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("请求完成: " + uri + " | 耗时: " + duration + "ms");
        }
    }
    @Override
    public void destroy() {
        System.out.println("日志过滤器被销毁");
    }
}
/**
 * 响应包装类(如果需要记录响应相关信息)
 */
class ResponseWrapper extends HttpServletRequestWrapper {
    private ServletResponse response;
    public ResponseWrapper(HttpServletRequest request, ServletResponse response) {
        super(request);
        this.response = response;
    }
}

案例4:XSS(跨站脚本攻击)过滤过滤器

package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.regex.Pattern;
/**
 * XSS过滤器 - 防止跨站脚本攻击
 */
@WebFilter(urlPatterns = "/*")
public class XSSFilter implements Filter {
    // XSS攻击模式
    private static final Pattern[] XSS_PATTERNS = {
        Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
        Pattern.compile("src[\r\n]*=[\r\n]*\\'(.*?)\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
        Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
        Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
        Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
        Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
        Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
        Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
        Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
        Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE)
    };
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("XSS过滤器初始化");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        // 使用自定义的请求包装类来过滤参数
        chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response);
    }
    @Override
    public void destroy() {
        System.out.println("XSS过滤器被销毁");
    }
    /**
     * XSS请求包装类 - 对请求参数进行XSS过滤
     */
    static class XSSRequestWrapper extends HttpServletRequestWrapper {
        public XSSRequestWrapper(HttpServletRequest servletRequest) {
            super(servletRequest);
        }
        @Override
        public String[] getParameterValues(String parameter) {
            String[] values = super.getParameterValues(parameter);
            if (values == null) {
                return null;
            }
            int count = values.length;
            String[] encodedValues = new String[count];
            for (int i = 0; i < count; i++) {
                encodedValues[i] = stripXSS(values[i]);
            }
            return encodedValues;
        }
        @Override
        public String getParameter(String parameter) {
            String value = super.getParameter(parameter);
            return stripXSS(value);
        }
        @Override
        public String getHeader(String name) {
            String value = super.getHeader(name);
            return stripXSS(value);
        }
        /**
         * 过滤XSS攻击代码
         */
        private String stripXSS(String value) {
            if (value == null) {
                return null;
            }
            // 对特殊字符进行HTML转义
            value = value.replaceAll("&", "&amp;");
            value = value.replaceAll("<", "&lt;");
            value = value.replaceAll(">", "&gt;");
            value = value.replaceAll("\"", "&quot;");
            value = value.replaceAll("'", "&#x27;");
            value = value.replaceAll("/", "&#x2F;");
            // 移除XSS攻击模式
            for (Pattern pattern : XSS_PATTERNS) {
                value = pattern.matcher(value).replaceAll("");
            }
            return value;
        }
    }
}

配置方式

使用@WebFilter注解(推荐)

@WebFilter(
    urlPatterns = {"/api/*", "/admin/*"},
    filterName = "myFilter",
    initParams = {
        @WebInitParam(name = "param1", value = "value1"),
        @WebInitParam(name = "param2", value = "value2")
    }
)

使用web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 配置字符编码过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>com.example.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 配置登录验证过滤器 -->
    <filter>
        <filter-name>loginFilter</filter-name>
        <filter-class>com.example.filter.LoginFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>loginFilter</filter-name>
        <url-pattern>/admin/*</url-pattern>
        <url-pattern>/user/*</url-pattern>
    </filter-mapping>
    <!-- 配置日志过滤器 -->
    <filter>
        <filter-name>loggingFilter</filter-name>
        <filter-class>com.example.filter.LoggingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>loggingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

过滤器链执行顺序

// 过滤器执行顺序取决于配置顺序(web.xml)或类名(@WebFilter)
@WebFilter(urlPatterns = "/*", filterName = "Filter1")
@Order(1)  // 指定执行顺序
public class Filter1 implements Filter { ... }
@WebFilter(urlPatterns = "/*", filterName = "Filter2")
@Order(2)
public class Filter2 implements Filter { ... }
// 执行顺序:Filter1 -> Filter2 -> Servlet -> Filter2 -> Filter1

Maven依赖配置

<dependencies>
    <!-- Servlet API -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- 如果需要使用注解方式 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

最佳实践建议

  1. 性能考虑:避免在过滤器中进行耗时操作
  2. 线程安全:过滤器是单例的,注意线程安全性
  3. 异常处理:适当处理异常,避免影响整个请求链
  4. 资源清理:在destroy()方法中释放资源
  5. 配置灵活:使用初始化参数提高配置灵活性
  6. 日志记录:适当记录日志便于调试

常见问题排查

  1. 过滤器未执行:检查URL模式配置是否正确
  2. 编码问题:确保编码过滤器在其他过滤器之前执行
  3. 死循环:避免过滤器自身再次调用自身
  4. 性能问题:尽量减少请求/响应的包装操作

这些案例涵盖了主要的过滤器开发场景,可以根据实际需求进行修改和扩展。

抱歉,评论功能暂时关闭!