现象
多个接口的请求参数中文偶尔部分乱码,乱码位置不固定

分析
根据日志分析,参数在达到应用服务时乱码,在应用服务前还有Nginx和网关,初步猜测网关出问题的可能性较大
部分乱码,估计是字节级别的处理问题,在网关项目里全局搜索stream 和byte。经过排查,在一处代码出现byte转字符串处理,代码如下

 @Override
public Flux<DataBuffer> getBody()
{
    Flux<DataBuffer> body = super.getBody();
    return body.map(dataBuffer -> {
        byte[] content = new byte[dataBuffer.readableByteCount()];
        dataBuffer.read(content);
        DataBufferUtils.release(dataBuffer);
        String bodyStr = new String(content, StandardCharsets.UTF_8);
        // 防xss攻击过滤
        bodyStr = EscapeUtil.clean(bodyStr);
        // 转成字节
        byte[] bytes = bodyStr.getBytes();
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    });
}

当请求体过大时,网关会对请求体进行切割,切分为多个字节数组,一个中文字符占2~3个字节,如果切割数组时恰巧将一个中文的多个字节切分到2个数组中(一部分前,一部分后),再将切分后的字节数组转字符串时,就会出现乱码问题。
解决
修改这部分

 @Override
public Flux<DataBuffer> getBody()
{
    Flux<DataBuffer> body = super.getBody();
    return body.buffer().map(dataBuffers -> {
        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
        DataBuffer join = dataBufferFactory.join(dataBuffers);
        byte[] content = new byte[join.readableByteCount()];
        join.read(content);
        DataBufferUtils.release(join);
        String bodyStr = new String(content, StandardCharsets.UTF_8);
        // 防xss攻击过滤
        bodyStr = EscapeUtil.clean(bodyStr);
        // 转成字节
        byte[] bytes = bodyStr.getBytes();
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    });
}

如何复现
知道问题问题为什么出现,就容易复现了,只需要将请求体多塞点中文,中文越多,请求体越大,越容易复现。

后记
项目作者也修复了这个问题:修复响应体过大出现的乱码问题

转自:https://www.jianshu.com/p/f942f423c3d7

上一篇 下一篇