What is `HttpServletRequestWrapper` does?

HttpServletRequestWrapperJava Servlet API 中的一个类。作为 HttpServletRequest 接口的包装器实现。

该类设计为装饰者模式的一部分,允许开发人员通过包装现有的 HttpServletRequest对象来定制或修改请求行为。
比如:
1、过滤或修改请求参数
2、添加或删除请求头信息
3、实现请求级的安全控制,如防止 XSS(跨站脚本攻击)或 SQL 注入等安全风险
4、转换请求体数据
5、修改请求URI或其他请求属性

在这里插入图片描述
样例:获取requestBody的内容
2、添加或删除请求头信息
3、实现请求级的安全控制,如防止 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.*;

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
                     * @throws IOException 如果发生输入/输出错误
                     */
                    @Override
                    public int read() throws IOException {
                        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;
    }
}
# 使用
FilterHttpRequestBodyWrapper filterHttpRequest = new FilterHttpRequestBodyWrapper(httpRequest);
String body = filterHttpRequest.getBodyByteArr();
filterHttpRequest.setAttribute("requestBodyData", body);

# 继续执行filter链
chain.doFilter(filterHttpRequest, response);

样例:获取requestBody的内容
4、转换请求体数据

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
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值