如何构建一个可重复读流 InputStream 的 HttpServletRequest?
之前在某公司工作的时候,领导要求所有前端向后端传递的参数都要经过前端加密,后端解密。说一句题外话:个人认为这种操作纯属脱裤子放屁,没啥用。因为前端代码都是公开的,无论你采用对称加密、非对称加密,或者摘要算法验签等等,对于稍懂技术的人来说,稍稍分析一下就能找到前端加密的方法,然后直接用相同的方式加密就行,所以这就是障眼法,只能骗骗不懂技术的人。不过领导的要求吗,既然定下来了,那么我们总要服从。因为每个方法都需要有这个解密或者验签的过程,我们自然而然想要到了通过 Filter、Interceptor 或者 AOP 等技术统一来做,不可能在各个方法中做这件事,在但是我们都知道,对于 post、put 等请求,参数都是放在请求体中的,需要通过流读出来,而流是不可以重复读的,所以我们应该怎么来解决这个问题,来构造一个可以重复读流 InputStream 的 HttpServletRequest。
解决方法:使用自定义类来缓存 stream 即可 RequestWrapper 类:缓存字节数据
package cn.bridgeli.filter; import cn.bridgeli.utils.http.HttpHelper; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; /** * 构建可重复读取inputStream的request * * @author BridgeLi */ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public RepeatedlyRequestWrapper(HttpServletRequest request) { super(request); body = HttpHelper.getBodyString(request).getBytes(StandardCharsets.UTF_8); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public int available() throws IOException { return body.length; } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } } package cn.bridgeli.utils.http; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; /** * 通用http工具封装 * * @author BridgeLi */ public class HttpHelper { private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); public static String getBodyString(ServletRequest request) { StringBuilder sb = new StringBuilder(); try (InputStream inputStream = request.getInputStream()) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { LOGGER.error("getBodyString出现问题!", e); } return sb.toString(); } }
然后,可以在 Servlet 或 Filter 中使用 RepeatableFilter 替换原始的 HttpServletRequest。
package cn.bridgeli.filter; import cn.bridgeli.filter.RepeatedlyRequestWrapper; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * Repeatable 过滤器 * * @author BridgeLi */ public class RepeatableFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = null; if (request instanceof HttpServletRequest) { requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request); } if (null == requestWrapper) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } @Override public void destroy() { } }
这样就解决了。
其实,对于可重复读流 InputStream 的 HttpServletRequest,除了我前公司领导的这个不合理需求,但是在实际工作中还是很有意义的,例如统一记录日志打一下参数,或者通过 AOP 做一些其他的业务等等。
最后,我在前公司的时候那时候还没有 ChatGPT,但是在 2022 年的 11 月底,ChatGPT 横空出世,现在把这个需求贴到 ChatGPT 中,ChatGPT 哗哗的就能解决了,方式方法一样。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/758
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
近期评论