SecretofSerializationliujia[2011Q1][有关java序列化的一些琐事]目录为什么要有序列化..................................................................................................................................................3序列化都干些啥......................................................................................................................................................4首要问题:反序列化时如何生成实例................................................................................................................5是不是所有的类都需要序列化.............................................................................................................................6哪些东西需要序列化.............................................................................................................................................7用transient保护敏感信息..................................................................................................................................15序列化算法............................................................................................................................................................15定制自己的序列化算法.......................................................................................................................................24严重漏洞:刻舟求剑...........................................................................................................................................32单例模式不过是浮云...........................................................................................................................................34同引用实例化问题...............................................................................................................................................37常见的序列化攻击...............................................................................................................................................39百家争鸣:hessian和mina也玩序列化...........................................................................................................43工作中需要注意的点...........................................................................................................................................49附录1ObjectOutputStream和ObjectInputStream...........................................................................................50附录2Serializable中的隐藏方法一览...............................................................................................................51参考文献................................................................................................................................................................52序列化的秘密为什么要有序列化任何事物的产生都是有其原因的,序列化也不例外。要说到序列化的起源,还得从最基本的java程序说起(本文介绍的是java序列化)。啥是java程序,简单来说就是一个进程一块内存,进程根据类生成一堆实例,并放到内存中,这些实例进行交互运算,最终产生我们想要的结果。如下图所示:图:java程序示意图这样就有了一个依赖关系,即内存中的对象依赖于内存,而内存又依赖于进程。这个很好解释,如果进程停止了,内存就被回收了;内存被回收了,内存中的对象也就destroy了。这就产生了很大的限制,举几个例子:1、一个程序停止了,内存中的对象也没了。但如果我想在新的程序中使用之前那个对象,该怎么办?2、网络上传输对象(网络两端不共享内存)3、远程接口调用,两端在各自的虚拟机中运行,当然内存也不共享,那入参和结果如何传递?4、….序列化就是来解决这些问题的。虽然内存不共享,但我们可以将对象转化为一段字节序列,并放到流中,接下来就交给I/O,可以存储在文件中、可以通过网络传输…;当我们想用到这些对象的时候,再通过I/O,从文件、网络上读取字节序列,根据这些信息重建对象。序列化都干些啥上面已经介绍了需要序列化的原因,并简单说了一下序列化做的事情,概括起来就是下面这张图:obj0x01….0x02outputStream序列化0x01….0x02obj反序列化文件数据库网络inputStream顺便也解释了一下“反序列化”,反序列化就是序列化的逆过程,将对象还原的过程。不要认为“反序列化”不重要,如果没有“反序列化”,那么“序列化”是没有任何意义的,光保存对象而用不上,这傻事谁干啊。再简单地照着这个图说下序列化的过程,不过这么几件事:1、根据某种序列化算法(详见序列化算法),将对象转换为字节序列2、将这个字节序列通过输出流写入到载体中(文件、数据库、网络等),详见附录1ObjectOutputStream和ObjectInputStream3、字节序列通过载体进行保存/传输4、当要用到这些对象的时候,程序通过输入流从载体中读取出字节序列5、根据某种反序列化算法,将字节序列转换为对象,详见序列化算法虽然过程很简单,但里面却有很多说道,下面就逐一介绍。首要问题:反序列化时如何生成实例为什么要谈这个问题呢,还是首要问题?有两个原因:1、正如上面说的,不能反序列化的序列化是没意义的,好像有点绕。再通俗点就是这个过程要可逆。看上面的图,对象序列化前是个“实例”。那么反序列化的时候,最终你也要生成一个实例。2、跟文章结构有关,后面的一些特性等,都是基于这个问题的。因此有必要先将这个问题说一下。下面来解答这个问题,那么如何生成实例呢,一般的做法是让每个类提供一个默认的构造方法(无参),等到反序列化的时候,自动调用这个构造方法来生成实例。看似蛮合理的、蛮简单的,但是这种方式有几个缺陷:1、侵入性太大,为了实现序列化,类还要做这么多事情。2、不安全,如果有的类不希望提供构造方法给外界调用。那么序列化的这个“无理”要求将引起灾难。因此这个方案行不通,只能另寻出路。这里不绕弯子了,直接说我们的做法:很简单,不用类去显示声明无参构造方法,而是通过一种语言之外的对象创建机制。当然也是通过构造方法,只不过这个构造方法不用我们来显示声明。从底层源码来看,生成实例时调用了java.reflect.Constructor的newInstance()方法:////////用反射生成实例用反射生成实例用反射生成实例用反射生成实例publicTnewInstance(Object...initargs)throwsInstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException{//…此次省略200个字return(T)constructorAccessor.newInstance(initargs);constructorAccessor.newInstance(initargs);constructorAccessor.newInstance(initargs);constructorAccessor.newInstance(initargs);}而最后实际上是调用了ConstructorAccessor的newInstance()方法,而再往下,ConstructorAccessor调用了本地方法,这个我们就不继续挖了。这里就有两个问题需要注意:1、通过序列化可以任意创建实例,不受任何限制。这个后面会说到,详见单例模式不过是浮云2、由于不调用类自己的构造器,可能通过构造器来保证的一些限制条件就很难满足。比如,有两个参数max,min,构造时必须满足maxmin。这个后面也会说到,详见常见的序列化攻击是不是所有的类都需要序列化说完了首要问题,再来说一个原则问题“是不是所有的类都需要序列化?”。如果是,则序列化就是类的基本功能,类似于呼吸之于人类。如果这样的话,序列化就彻底透明了,但可惜不是,原因如下:1、安全问题:安全问题:安全问题:安全问题:有的类是敏感的类,里面的数据不能公开。而实现了序列化就很容易被破解,可以说没有秘密。所以有隐私性不能保证。2、资源问题:资源问题:资源问题:资源问题:序列化可以任意生成实例而不受限制(上面说到的),如果有的对象生成的实例是受限制的,比如只能生成10个实例,或者是单例的,这个很难保证。所以不是所有的类都需要序列化,那么这就要提供一个接口/标识符,需要序列化的类要贴上标识符。这个标识符就是Serializable或Externalizable(详见定制自己的序列化算法),实现了任何一个接口就代表可以被序列化。这里随便提一下,java有个工具可以查看是否能够序