专栏首页BennyhuoJava 17 更新(12):支持上下文的序列化过滤器,又一次给序列化打补丁

Java 17 更新(12):支持上下文的序列化过滤器,又一次给序列化打补丁

关键词:Java Java17

Java 的序列化机制虽然有些问题,不过毕竟亲儿子,更新怎么能落下呢。

接下来我们介绍 Java 17 合入的最后一个还没介绍的提案:JEP 415: Context-Specific Deserialization Filters,这是一条对于反序列化的更新。

Java 的序列化机制一向为人诟病,以至于 Effective Java 里面专门有几条讲 Java 序列化机制的,并且结论是“不要用它”。

这玩意你说咋还不废弃了呢。居然还在不断为了反序列化的安全性修修补补。

算了,我猜你们大概率用不到,不介绍了。

好吧,其实不是,这玩意儿还是很常用的,所以还是介绍一下吧。

故事还要追溯到 Java 9,当时为了解决反序列化的数据的安全性问题,Java 提供了反序列化的过滤器,允许在反序列化的时候对数据做检查,这个过滤器就是 ObjectInputFilter。

public interface ObjectInputFilter {

    /**
     * @return  {@link Status#ALLOWED Status.ALLOWED} if accepted,
     *          {@link Status#REJECTED Status.REJECTED} if rejected,
     *          {@link Status#UNDECIDED Status.UNDECIDED} if undecided.
     */
    Status checkInput(FilterInfo filterInfo);
}

它最关键的方法就是这个 checkInput,返回值则是一个枚举。

在每一个 ObjectInputStream 实例被创建的时候都会创建一个过滤器与之对应:

Java 16

public ObjectInputStream(InputStream in) throws IOException {
    ...
    serialFilter = ObjectInputFilter.Config.getSerialFilter();
    ...
}

这个过滤器实际上是 JVM 全局的过滤器,可以通过系统属性 jdk.serialFilter 来配置,也可以通 ObjectInputFilter.Config#setSerialFilter 来设置。

在 ObjectInputStream 创建出来之后,我们也可以通过它的 setObjectInputFilter 来对这个实例单独设置自定义的过滤器。

以上的特性都是 Java 9 引入的,下面我们看看 Java 17 的更新:

Java 17

public ObjectInputStream(InputStream in) throws IOException {
    ...
    serialFilter = Config.getSerialFilterFactorySingleton().apply(null, Config.getSerialFilter());
    ...
}

其实这段代码已经很明确的展示了改动之处,那就是 getSerialFilterFactorySingleton 返回的这个对象对原有的全局过滤器做了个变换。这个对象实际上是个 BinaryOperator<ObjectInputFilter>,实现这个 FilterFactory 就可以通过实现 apply 方法来完成对原有过滤器的修改:

@Override
public ObjectInputFilter apply(ObjectInputFilter objectInputFilter, ObjectInputFilter objectInputFilter2) {
    return ...;
}

所以如果你乐意,你可以随机返回 objectInputFilter 或者返回 objectInputFilter2(草率。。。),也可以把它俩串联或者并联起来。换句话讲,我们除了可以通过设置全局过滤器,以及单独为每一个 ObjectInputStream 实例设置过滤器以外,还可以设置一个操纵过滤器的对象,这个对象可以根据上下文来判断具体返回什么样的过滤器。

接下来我们再看一下提案当中给出的例子(实际的 JDK API 与提案的例子有些调整,以下代码是调整之后的):

public class FilterInThread implements BinaryOperator<ObjectInputFilter> {

    private final ThreadLocal<ObjectInputFilter> filterThreadLocal = new ThreadLocal<>();

    public ObjectInputFilter apply(ObjectInputFilter curr, ObjectInputFilter next) {
        if (curr == null) {
            var filter = filterThreadLocal.get();
            if (filter != null) {
                filter = ObjectInputFilter.rejectUndecidedClass(filter);
            }
            if (next != null) {
                filter = ObjectInputFilter.merge(next, filter);
                filter = ObjectInputFilter.rejectUndecidedClass(filter);
            }
            return filter;
        } else {
            if (next != null) {
                next = ObjectInputFilter.merge(next, curr);
                next = ObjectInputFilter.rejectUndecidedClass(next);
                return next;
            }
            return curr;
        }
    }

    ...
}

这个例子其实不复杂,我最初看的时候反而被一堆注释给搞得晕头转向,所以我决定把注释都删了给你们看。。。

它的逻辑简单来说就是 apply 的时候如果 curr 为 null,就从的 ThreadLocal 当中取出当前线程对应的过滤器与 next 进行合并,否则就用 curr 与 next 合并。

但通过前面阅读代码,我们已经知道 curr 在 ObjectInputStream 创建的时候传入的一定是 null(只有在后面调用 ObjectInputStream#setObjectInputFilter 的时候 curr 才会是之前已经创建的过滤器),因此这个 FilterInThread 就可以在 ObjectInputStream 创建的时候为它添加一个线程特有的过滤器,也就是上下文相关的过滤器了。

实际上例子里面还提供了一个临时切换过滤器的方法:

public class FilterInThread implements BinaryOperator<ObjectInputFilter> {
    ...
    
    public void doWithSerialFilter(ObjectInputFilter filter, Runnable runnable) {
        var prevFilter = filterThreadLocal.get();
        try {
            filterThreadLocal.set(filter);
            runnable.run();
        } finally {
            filterThreadLocal.set(prevFilter);
        }
    }
}

我们可以通过调用 doWithSerialFilter 来实现将 runnable 的 run 当中所有直接创建的 ObjectInputStream 都将应用传入的这个 filter 作为自己的上下文过滤器。

有意思吧。不过一点儿也不直接。挺简单的一个东西竟然能搞得这么别扭。。。

讲到这儿,我们总算是把 Java 17 的主要更新介绍了一遍。除了这些大的更新以外,还有一些小的 Bugfix 和优化,我就不一一列举了。


文章分享自微信公众号:
Kotlin

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!

作者:Benny Huo
原始发表时间:2021-10-15
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • Java 17 新特性,快到起飞?惊呆了!

    本书最新版,主要更新了在JDK 17发布的的新特性,JDK 17是继JDK11之后的一个新的长期支持版本,免费使用至2024年9月,同时会持续更新到2029年9...

    公众号:大数据羊说
  • Java 17 新功能介绍(LTS)

    Java 17 在 2021 年 9 月 14 日正式发布,Java 17 是一个长期支持(LTS)版本,这次更新共带来 14 个新功能。

    未读代码
  • JDK的第三个LTS版本JDK17来了

    2021年9月JDK17发布了,JDK17是最新的一个LTS版本。所谓LTS版本就是可以得到至少八年产品支持的版本。从2014年的JDK8,到2018年的JDK...

    程序那些事
  • JDK的第三个LTS版本JDK17来了

    2021年9月JDK17发布了,JDK17是最新的一个LTS版本。所谓LTS版本就是可以得到至少八年产品支持的版本。从2014年的JDK8,到2018年的JDK...

    程序那些事
  • 重磅!JDK 17 发布,Oracle 宣布从 JDK 17 开始正式免费。。

    点击关注公众号,Java干货及时送达 上一版:JDK 16 正式发布,一次性发布 17 个新特性…不服不行! ---- JDK 17 正式发布+免费 牛逼啊,...

    Java技术栈
  • 走进Java接口测试之fastjson指南

    在上文 走进Java接口测试之理解JSON和XML基础 我们介绍了 JSON 的基础知识,本文我们深入研究阿里巴巴的开源 JSON 解析库 fastjson。

    高楼Zee
  • 针对RMI的反序列化攻击

    RMI调用由三部分构成:服务端,客户端,注册端。而在RMI传输数据时,数据是以序列化的形式进行传输的,这就意味着RMI调用中存在反序列化的操作,这就给了反序列化...

    ConsT27
  • Java17来了!YYDS!

    上个周末我发了一篇名为:Spring 官宣,干掉 Spring 5.3.x! 的技术快报,这篇文章主要介绍了 Spring Framework 6 和 Spri...

    Guide哥
  • 详谈JAVA中的file类与IO流

    File类 位于java.io包 构造方法: File(String parent, String child) new file("d:\\","a.txt...

    HUC思梦
  • 一个关于解决序列化问题的编程技巧

    在前一篇文章中我曾经说过,现在正在做一个小小的框架以实现采用统一的API实现对上下文(Context)信息的统一管理。这个框架同时支持Web和GUI应用,并支持...

    蒋金楠
  • 小厂后端十连问(附答案)

    大家好,在此分享一份面试真题,我整理了一下答案给大家。如果有不正确的,欢迎指出哈,一起进步。

    用户1263954
  • SpringBoot入门教程,吃透这些超过90%的Java面试者

    动力节点王鹤老师的SpringBoot入门系列课程,通俗易懂,基于SpringBoot2.4版本讲解。

    动力节点铁杆粉丝儿
  • 必须知道的RPC内核细节(值得收藏)!!!

    微服务分层架构,之前聊得很多了,微服务离不开RPC框架,RPC框架的原理、实践及细节,今天和大家聊一聊。 文章较长,1万字左右,建议提前收藏。 服务化有什么...

    架构师之路
  • 重磅!JDK 17 发布,Oracle 宣布 JDK 17 可以免费商用了。。

    果然,JDK 17 还是如期发布了,2021年09月14日。巧了,和苹果发布会是一天,不知道是不是互相在蹭热度,哈哈哈~~~

    沉默王二
  • 3分钟带你品尝新鲜出炉的Java 17,看完就知道香不香!

    Java已经过了20个年头了,Java8之后,JDK的发行计划变了不少,半年一小聚,三年一大闹。根据Oracle官方的的文档,长期支持的Java版本是Java1...

    纯洁的微笑
  • 2021年春招,Java后端最全面试攻略,吃透25个技术栈

    小编分享的这份春招Java后端开发面试总结包含了JavaOOP、Java集合容器、Java异常、并发编程、Java反射、Java序列化、JVM、Redis、Sp...

    Java程序猿
  • 面霸篇:Dubbo 夺命 17 问

    Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

    码哥字节
  • 《面试八股文》之 Dubbo 卷

    今天就开启《面试八股文》系列的第一版-RPC王者Dubbo,在后续的《面试八股文》系列还将继续推出mysql,spring,并发,redis,kafka,zoo...

    终码一生

扫码关注腾讯云开发者

领取腾讯云代金券