Spring的FastJsonHttpMessageConverter遇到非法字符的时候会导致异常

oalqel3c  于 6个月前  发布在  Spring
关注(0)|答案(1)|浏览(122)

在使用FastJson做HttpMessageConverter的时候,如果客户端(嵌入式设备),传来非法字符(出现问题的是编码错误的表情包,本应使用4个字节的UTF-8编码,客户端将其编码为两个3字节的字符,Java中无法识别),在将ResponseBody以字符串形式获取时(@requestbody String data)会报Resolved exception caused by handler execution: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing。

详细错误信息如下:

Error parsing HTTP request header

java.io.EOFException: null
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1289) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1223) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
	at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:729) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
	at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:368) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.35.jar:8.5.35]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)

使用的依赖版本为Spring boot 1.5.18,FastJson 1.2.54

经过我的测试,这个问题是必现的,测试用例如下:

private static byte[] bytes = {
            (byte) 0x7B, (byte) 0x22, (byte) 0x6E, (byte) 0x61, (byte) 0x6D,
            (byte) 0x65, (byte) 0x22, (byte) 0x3A, (byte) 0x22, (byte) 0xE5,
            (byte) 0xBF, (byte) 0x97, (byte) 0xE5, (byte) 0x9B, (byte) 0xBD,
            (byte) 0xED, (byte) 0xA0, (byte) 0xBD, (byte) 0xED, (byte) 0xB8,
            (byte) 0x8F, (byte) 0xED, (byte) 0xA0, (byte) 0xBD, (byte) 0xED,
            (byte) 0xB8, (byte) 0x92, (byte) 0xED, (byte) 0xA0, (byte) 0xBD,
            (byte) 0xED, (byte) 0xB8, (byte) 0x93, (byte) 0xED, (byte) 0xA0,
            (byte) 0xBD, (byte) 0xED, (byte) 0xB8, (byte) 0x94, (byte) 0xED,
            (byte) 0xA0, (byte) 0xBD, (byte) 0xED, (byte) 0xB8, (byte) 0x9E,
            (byte) 0xED, (byte) 0xA0, (byte) 0xBD, (byte) 0xED, (byte) 0xB8,
            (byte) 0x9E, (byte) 0xED, (byte) 0xA0, (byte) 0xBD, (byte) 0xED,
            (byte) 0xB8, (byte) 0x9E, (byte) 0xED, (byte) 0xA0, (byte) 0xBD,
            (byte) 0xED, (byte) 0xB8, (byte) 0xA0, (byte) 0x22, (byte) 0x7D
    };

直接发送这个字节数组,就会导致异常,如果将其创建为String,Java会将非法字符过滤,就不会报异常

过滤前
7b226e616d65223a22e5bf97e59bbdeda0bdedb88feda0bdedb892eda0bdedb893eda0bdedb894eda0bdedb89eeda0bdedb89eeda0bdedb89eeda0bdedb8a0227d
过滤后
7b226e616d65223a22e5bf97e59bbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbd227d
过滤后的String
{"name":"志国����������������"}

Jackson和Gson不会出现这个问题

查看fastjson的源码后,发现FastJsonHttpMessageConverter会将请求内容的body字节数组直接传入解析方法,进一步测试,如果将此字节数组直接解析为Object,调用此Object的toString,即会报空指针异常。

Object o = JSONObject.parse(bytes);
System.out.println(o.toString());

此时返回的o为null,这种情况显然不是很合理

ijxebb2r

ijxebb2r1#

进一步看代码后发现,JSON.parse这个函数中

public static <T> T parseObject(byte[] bytes, int offset, int len, Charset charset, Type clazz, Feature... features) {
        if (charset == null) {
            charset = IOUtils.UTF8;
        }

        String strVal;
        if (charset == IOUtils.UTF8) {
            char[] chars = allocateChars(bytes.length);
            int chars_len = IOUtils.decodeUTF8(bytes, offset, len, chars);
            if (chars_len < 0) {
                return null;
            }

            strVal = new String(chars, 0, chars_len);
        } else {
            if (len < 0) {
                return null;
            }

            strVal = new String(bytes, offset, len, charset);
        }

        return parseObject(strVal, clazz, features);
    }

UTF-8 decode的结果小于0的时候,结果是为空,直接解析的时候过程也是一样的。但是这个IOUtils.decodeUTF8的结果显然与Java对于非法字符的处理方案不同,这个函数会直接判定为错误,而Java新建String的时候会将它转换为固定的错误符号

相关问题