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编码,
通过两个地方的设置,中文字符不再乱码

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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值