HttpServletRequestWrapper的使用场景总结

[Q&A] jakarta.servlet.http.HttpServletRequestWrapper

该类作为 HttpServletRequest 接口的包装器实现。并且设计为一个装饰器模式的实现,主要用于在不修改原有请求对象的基础上,扩展或修改HttpServletRequest对象的行为,而不需要直接实现 HttpServletRequest 接口。

[Q&A] 使用场景

1・安全过滤:可以在请求到达目标Servlet之前,对请求参数进行安全检查或过滤敏感信息。
2・日志记录:记录请求的详细信息,如URL、参数、Header等,用于监控或审计。
3・参数修改:动态修改请求中的参数或添加新的参数,以适应特定业务需求。
4.请求模拟:在单元测试中,可以创建一个模拟的HttpServletRequest对象来测试Servlet的行为。

样例:获取requestBody的内容

添加或删除请求头信息
实现请求级的安全控制,如防止 XSS(跨站脚本攻击)或 SQL 注入等安全风险

import com.zhangziwa.practisesvr.utils.stream.StreamIUtils;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.text.StringEscapeUtils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class FilterHttpRequestBodyWrapper extends HttpServletRequestWrapper {
    private final byte[] bodyByteArr; // 字节数组
    private final ByteArrayInputStream byteArrayInputStream; // 从字节数组读取数据的输入流
    private BufferedReader bufferedReader; // getReader()
    private ServletInputStream servletInputStream; // getInputStream()

    public FilterHttpRequestBodyWrapper(HttpServletRequest request) throws IOException {
        super(request);
        bodyByteArr = StreamIUtils.readStream2Bytes(request.getInputStream());
        byteArrayInputStream = new ByteArrayInputStream(bodyByteArr);
    }

    public String getBodyByteArr() throws UnsupportedEncodingException {
        return new String(bodyByteArr, getCharacterEncoding());
    }

    /**
     * 覆盖父类方法,实现获取参数时自动对参数值进行XSS攻击过滤。
     *
     * @param name 参数名
     * @return 如果参数存在,则返回经过HTML转义的、已过滤XSS攻击的参数值;若参数不存在,则返回null
     */
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value != null) {
            // 使用StringEscapeUtils.escapeHtml4对请求参数进行XSS攻击过滤
            return StringEscapeUtils.escapeHtml4(value);
        }
        return null;
    }

    /**
     * 重写父类的getCharacterEncoding方法,获取请求的字符编码。
     * 若当前请求的字符编码未设置或为空,则默认返回"utf-8"作为字符编码。
     *
     * @return 请求的字符编码,若原编码为空则返回"utf-8"
     */
    @Override
    public String getCharacterEncoding() {
        String encoding = super.getCharacterEncoding();
        return encoding == null ? "utf-8" : encoding;
    }

    /**
     * 重写父类或接口中的getReader方法,提供一个BufferedReader对象。
     *
     * @return 返回一个根据请求体内容和字符编码方式创建的BufferedReader对象
     */
    @Override
    public BufferedReader getReader() {
        if (bufferedReader == null) {
            try {
                // 封装一个BufferedReader对象以提高读取效率
                bufferedReader = new BufferedReader(new InputStreamReader(byteArrayInputStream, getCharacterEncoding()));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("Unsupported encoding: ", e);
            }
        }
        return bufferedReader;
    }

    /**
     * 重写父类的 getInputStream 方法,提供一个自定义的 ServletInputStream 实例,
     * 该实例从内部的 byteArrayInputStream 中读取数据,并支持监听器模式。
     *
     * @return 自定义的 ServletInputStream 实例,用于读取请求体数据
     */
    @Override
    public ServletInputStream getInputStream() {
        // 确保 servletInputStream 的初始化在多线程环境下是安全的
        if (servletInputStream == null) {
            synchronized (this) {
                // 创建并初始化一个 ServletInputStream 子类实例
                servletInputStream = new ServletInputStream() {
                    /**
                     * 从内部的 byteArrayInputStream 中读取下一个字节数据
                     *
                     * @return 下一个可读字节,如果已到达流末尾则返回 -1
                     */
                    @Override
                    public int read() {
                        return byteArrayInputStream.read();
                    }

                    /**
                     * 判断是否已经读取完所有数据,即 byteArrayInputStream 是否还有可用数据
                     *
                     * @return 如果没有更多数据可供读取,则返回 true;否则返回 false
                     */
                    public boolean isFinished() {
                        return byteArrayInputStream.available() == 0;
                    }

                    /**
                     * 指示此输入流是否准备好进行读取操作
                     *
                     * @return 始终返回 true,表示此输入流始终处于就绪状态
                     */
                    public boolean isReady() {
                        return true;
                    }

                    /**
                     * 设置 ReadListener 监听器,用于异步读取数据。此处未实现具体逻辑。
                     *
                     * @param readListener 用于处理数据读取事件的 ReadListener 实例
                     */
                    @Override
                    public void setReadListener(ReadListener readListener) {
                    }
                };
            }
        }
        return servletInputStream;
    }
}

样例使用

public class RequestCheckFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest)) {
            chain.doFilter(request, response);
            return;
        }
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        FilterHttpRequestBodyWrapper filterHttpRequest = new FilterHttpRequestBodyWrapper(httpRequest);
        String body = filterHttpRequest.getBodyByteArr();
        filterHttpRequest.setAttribute("requestBodyData", body);
        
        chain.doFilter(filterHttpRequest, response);
    }
}

样例:获取requestBody的内容

转换请求体数据

StudentParamHttpRequestWrapper 是一个自定义的 HttpServletRequest 包装类,主要用于在处理 HTTP 请求时对学生的特定参数进行封装和预处理。它主要用于解决以下问题:
1・对请求参数的统一管理和验证,例如检查学号(studentId)、姓名(name)等字段的有效性。
2・在过滤器、拦截器或控制器中便捷地访问和操作学生相关信息,无需重复解析原始请求参数。

import com.zhangziwa.practisesvr.model.Student;
import com.zhangziwa.practisesvr.utils.JsonUtils;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/
 * StudentParamHttpRequestWrapper 是一个自定义的 HttpServletRequest 包装类,它继承或实现 HttpRequestWrapper 类,
 * 主要用于在处理 HTTP 请求时对学生的特定参数进行封装和预处理。此类允许开发者方便地获取、验证以及修改请求中的学生相关参数,
 * 提供了一种集中管理与学生信息相关的请求参数的方式。
 * 它主要用于解决以下问题:
 * 1. 对请求参数的统一管理和验证,例如检查学号(studentId)、姓名(name)等字段的有效性。
 * 2. 在过滤器、拦截器或控制器中便捷地访问和操作学生相关信息,无需重复解析原始请求参数。
 * 注意:此包装类的具体实现可能需要根据实际项目需求来调整。
 */
public class StudentParamHttpRequestWrapper extends HttpServletRequestWrapper {
    private final String body;

    public StudentParamHttpRequestWrapper(HttpServletRequest request, Student vo) {
        super(request);
        this.body = JsonUtils.toJson(vo);
    }

    @Override
    public String getCharacterEncoding() {
        String encoding = super.getCharacterEncoding();
        return encoding == null ? "utf-8" : encoding;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes(getCharacterEncoding()));
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }

            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

样例使用

# 使用
StudentParamHttpRequestWrapper filterHttpRequest = new StudentParamHttpRequestWrapper(httpRequest, new Student());
chain.doFilter(filterHttpRequest, response);

参考

[Ref] StreamIUtils 共通方法最佳实践

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HttpServletRequestWrapper 是一个用来增强 HttpServletRequest 的类,它实现了 HttpServletRequest 接口,并且可以通过继承它来对 HttpServletRequest 进行增强。 下面是一个使用 HttpServletRequestWrapper 的示例代码: ```java public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper { private Map<String, String[]> customParams; public CustomHttpServletRequestWrapper(HttpServletRequest request) { super(request); customParams = new HashMap<>(); } public void addParameter(String name, String value) { String[] values = customParams.get(name); if (values == null) { values = new String[] { value }; } else { values = Arrays.copyOf(values, values.length + 1); values[values.length - 1] = value; } customParams.put(name, values); } @Override public String getParameter(String name) { String[] values = customParams.get(name); if (values != null && values.length > 0) { return values[0]; } return super.getParameter(name); } @Override public String[] getParameterValues(String name) { String[] values = customParams.get(name); if (values != null && values.length > 0) { return values; } return super.getParameterValues(name); } } ``` 这个类继承了 HttpServletRequestWrapper,并且实现了添加参数和获取参数的方法。在 addParameter 方法中,我们将自定义的参数存储到 customParams 中;在 getParameter 和 getParameterValues 方法中,我们首先从 customParams 中获取参数值,如果没有则调用父类的方法获取。 使用这个类可以很方便地对 HttpServletRequest 进行增强,比如在参数中添加一些自定义参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值