HttpServletRequestWrapper拦截body并解决编码问题

HttpServletRequestWrapper拦截body并解决编码问题

这个话题来自于做互联网接口时对数据签名引发的问题。数据签名主要是为了验证数据的完整性,同时起到身份验证的作用。
post请求body体里的内容,是以流的形式存在的,流不能多次读取,做签名验证时拦截器要先取读取数据做校验,导致后面controller无法再次读取到数据。

1、自定义一个HttpServletRequestWrapper的子类,用于对request进行包装
package com.mrx.common.wrapper;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
@Slf4j
public class CustomRequestWrapper extends HttpServletRequestWrapper {
    private final String body;
    public CustomRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder sb = new StringBuilder(128);
        BufferedReader br = null;
        try {
            InputStream is = request.getInputStream();
            if (is != null) {
                //br = new BufferedReader(new InputStreamReader(is));
                /**
                 * 当中文乱码时
                 * tomcat配置中加
                 * -Dfile.encoding=UTF-8
                 */
                br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
                char[] charBuffer = new char[128];
                int byteRead = -1;
                while ((byteRead = br.read(charBuffer)) > 0) {
                    sb.append(charBuffer, 0, byteRead);
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw e;
        } finally {
            if (br != null) {
                br.close();
            }
        }
        body = sb.toString();
        if (log.isDebugEnabled()){
            log.debug("body:{}",body);
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

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

    public String getBody() {
        return body;
    }
}
2、定义一个过滤器,对需要进行签名校验的请求提前进行拦截
package com.mrx.filter;

import com.mrx.common.wrapper.CustomRequestWrapper;
import com.mrx.constants.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Order(3)
@Component
public class HttpServletFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        AntPathMatcher matcher = new AntPathMatcher();
        String uri = request.getRequestURI();
        if (matcher.match(Constants.API_URI_PREFIX, uri)) {
            CustomRequestWrapper requestWrapper;
            requestWrapper = new CustomRequestWrapper(request);
            if (requestWrapper == null){
                filterChain.doFilter(request, response);
            }else {
                filterChain.doFilter(requestWrapper, response);
            }
        } else {
            filterChain.doFilter(request, response);
        }
    }
}

3、后续读取

因为在CustomRequestWrapper提前读取流并把信息保存到了变量body中,后续再次从流中读取信息时,其实都是从body转化来的。body里面永远有值,可以不限次数的读取。

4、中文乱码问题

在实际应用中,部署到服务器后,签名验证失败。后续排查发现解析出来的数据中文是乱码。
br = new BufferedReader(new InputStreamReader(is,“UTF-8”)); 读取流的时候统一使用UTF8编码,在IDE调试时都可以顺利进行,到了服务器上的tomcat还是乱码,疑惑…

5、tomcat设置编码

在tomcat服务器上配置编码
-Dfile.encoding=UTF-8
在这里插入图片描述

6、总结

程序中设置UTF8编码
tomcat服务器设置UTF8编码,
通过两个地方的设置,中文字符不再乱码

小尾巴~~
只要有积累,就会有进步

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、付费专栏及课程。

余额充值