Dubbo Hessian2序列化问题

Posted rhwayfunn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo Hessian2序列化问题相关的知识,希望对你有一定的参考价值。

问题

定义了一个接口,返回值为Map<String, Byte>,consumer收到结果后,使用Byte value = map.get(key)获取结果,抛出了如下类型转换异常:
如下:


java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Byte

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

当时很奇怪,为什么返回值已经是Map<String, Byte>了,还会抛类型转换异常呢。

分析

首先想到的是通过debug确认返回的类型是否是Byte,设置断点后发现返回的Map的value的类型并不是Byte而是Integer,那么为什么会是Integer呢?
这里猜测这里很可能是dubbo序列化导致的,dubbo默认使用Hessaian2作为序列化协议,所以去看了下Hessian2的序列化实现,果不其然,byte会被反序列化成Integer

代码在Hessian2InputreadObject方法:


/**
   * Reads an arbitrary object from the input stream when the type
   * is unknown.
   */
  public Object readObject()
    throws IOException
  
    int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();

    switch (tag) 
    case 'N':
      return null;

    case 'T':
      return Boolean.valueOf(true);

    case 'F':
      return Boolean.valueOf(false);

      // direct integer
    case 0x80: case 0x81: case 0x82: case 0x83:
    case 0x84: case 0x85: case 0x86: case 0x87:
    case 0x88: case 0x89: case 0x8a: case 0x8b:
    case 0x8c: case 0x8d: case 0x8e: case 0x8f:

    case 0x90: case 0x91: case 0x92: case 0x93:
    case 0x94: case 0x95: case 0x96: case 0x97:
    case 0x98: case 0x99: case 0x9a: case 0x9b:
    case 0x9c: case 0x9d: case 0x9e: case 0x9f:

    case 0xa0: case 0xa1: case 0xa2: case 0xa3:
    case 0xa4: case 0xa5: case 0xa6: case 0xa7:
    case 0xa8: case 0xa9: case 0xaa: case 0xab:
    case 0xac: case 0xad: case 0xae: case 0xaf:

    case 0xb0: case 0xb1: case 0xb2: case 0xb3:
    case 0xb4: case 0xb5: case 0xb6: case 0xb7:
    case 0xb8: case 0xb9: case 0xba: case 0xbb:
    case 0xbc: case 0xbd: case 0xbe: case 0xbf:
      return Integer.valueOf(tag - BC_INT_ZERO);

      /* byte int */
    case 0xc0: case 0xc1: case 0xc2: case 0xc3:
    case 0xc4: case 0xc5: case 0xc6: case 0xc7:
    case 0xc8: case 0xc9: case 0xca: case 0xcb:
    case 0xcc: case 0xcd: case 0xce: case 0xcf:
      return Integer.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read()); //byte转换成了Integer

      //省略其他代码

为了更好说明Hessian2序列化和反序列的问题,可以使用其序列化和反序列化
测试代码如下:


import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.google.common.collect.Maps;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

public class Hessian2SerialzeTest 

    @Test
    public void test() throws IOException 
        Map<String, Byte> map = Maps.newHashMap();
        map.put("weather", (byte) 1);
        byte[] bytes = serialize(map);
        Map<String, Byte> object = (Map<String, Byte>) deserialize(bytes);
        //这里会抛异常
        Byte weather = object.get("weather");
        System.out.println(weather);
    

    public static byte[] serialize(Object obj) throws IOException 
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Hessian2Output ho = new Hessian2Output(os);
        byte[] cc = null;
        try 
            if(obj==null) throw new NullPointerException();
            ho.writeObject(obj);
            ho.flushBuffer();
            cc=os.toByteArray();
         catch (Exception e) 
            e.printStackTrace();
        finally
            ho.close();
        
        return cc;

    

    public static Object deserialize(byte[] by) throws IOException
        try 
            if(by==null) throw new NullPointerException();
            ByteArrayInputStream is = new ByteArrayInputStream(by);
            Hessian2Input hi = new Hessian2Input(is);
            return hi.readObject();
         catch (Exception e) 
            e.printStackTrace();
        
        return null;

    


运行该test,再一次抛出了Integer转Byte失败的异常,这就证明了我们的猜测,Hessian2序列化确实存在这个问题。
那么问题清晰了,解决就很自然了,要么换一种序列化协议,要么用Object接受Byte,然后把object强转成Byte(证实可行)。

以上是关于Dubbo Hessian2序列化问题的主要内容,如果未能解决你的问题,请参考以下文章

dubbo序列化

dubbo 各通讯协议比较

Dubbo爆反序列化漏洞,赶快升级到稳定版本!

Hessian2序列化问题

java原生序列化和Kryo序列化性能比较

Dubbo协议 —— dubbo://(缺省)