THM学习笔记——SQL注入

简介

SQL注入,通常称为SQLi,是对 Web 应用程序数据库服务器的攻击,导致执行恶意查询。当 Web 应用程序使用未经适当验证的用户输入与数据库通信时,攻击者有可能窃取、删除或更改私人和客户数据,还可以攻击 Web 应用程序的身份验证方法以访问私人或客户区域。

数据库是什么?

数据库是以有组织的方式电子存储数据的一种方法。数据库由数据库管理系统(DBMS)控制,DBMS 是数据库管理系统的缩写,DBMS 分为两类:关系型非关系型

在 DBMS 中,可以有多个数据库,每个数据库包含自己的一组相关数据。

你可以使用称为表的东西将此信息单独存储在数据库中,表由列和行组成,表的每个列都有一个唯一的id。

表是什么?

表由列和行组成,可以将表想象为一个网格,列横跨从左到右,包含单元格名称,行从上到下,每行都有实际数据。

列:

每一列,最好称为字段,都有每个表的唯一名称。创建列时,还会设置它将包含的数据类型,常见的类型包括整型、字符串或日期。某些数据库可以包含更复杂的数据,例如包含位置信息的地理空间数据。设置数据类型还确保不会存储不正确的信息,例如将字符串 "hello world" 存储在日期列中。如果发生这种情况,数据库服务器通常会生成错误消息。包含整数的列还可以启用自动增量功能;这为每一行数据提供一个随着每一行的增加而增长(增量)的唯一数字,这创建了所谓的字段,键字段对于每一行的数据都必须是唯一的,可以用于在 SQL 查询中找到确切的行。

行:

行或记录包含个别数据行。向表添加数据时,将创建一个新的行/记录,而删除数据时,将删除一行/记录。

关系型数据库与非关系型数据库:

关系型数据库将信息存储在表中,通常表之间有共享信息,它们使用列来指定和定义存储的数据,并使用行来实际存储数据。表通常包含一个具有唯一 ID(主键)的列,该 ID 将在其他表中用于引用它并在表之间建立关系,因此称为关系型数据库。

非关系型数据库有时称为 NoSQL,它不使用表、列和行来存储数据,不需要构建特定的数据库布局,因此每一行数据可以包含不同的信息,这为非关系型数据库提供了比关系型数据库更大的灵活性。一些常见的这类数据库是 MongoDB、Cassandra 和 ElasticSearch。

什么是 SQL?

SQL(结构化查询语言)是用于查询数据库的功能丰富的语言,这些 SQL 查询更好地称为语句。

尽管有些相似,但一些数据库服务器具有它们自己的语法和工作方式上的轻微变化。

值得注意的是,SQL 语法不区分大小写。

SELECT

select * from users;

第一个单词 SELECT 告诉数据库我们想检索一些数据,* 告诉数据库我们想要从表中接收所有列的数据。例如,表可能包含三列(id、username 和 password)。"from users" 告诉数据库我们想要从名为 users 的表中检索数据。最后,分号表示查询的结束。

下一个查询与上面的类似,但这次,与使用 * 返回表中的所有列不同,我们只请求用户名和密码字段。

select username,password from users;

下面的查询与第一个相似,但这次,与使用 * 选择表中的所有列不同,“LIMIT 1” 子句强制数据库只返回一行数据。将查询更改为 "LIMIT 1,1" 强制查询跳过第一个结果,然后 "LIMIT 2,1" 跳过前两个结果,依此类推。需要记住,第一个数字告诉数据库要跳过多少个结果,而第二个数字告诉数据库要返回多少行。

select * from users LIMIT 1;

最后,我们将利用 where 子句;这是我们可以通过返回与我们的特定子句匹配的数据,只返回与我们的特定子句匹配的数据的方式:

select * from users where username='admin';

这将仅返回用户名等于 admin 的行。

select * from users where username != 'admin';

这将仅返回用户名不等于 admin 的行。

select * from users where username='admin' or username='jon';

这将仅返回用户名等于 adminjon 的行。

select * from users where username='admin' and password='p4ssword';

这将仅返回用户名等于 admin,且密码等于 p4ssword 的行。

使用 like 子句允许你指定不是精确匹配的数据,而是以某些字符开始、包含或以某些字符结束的数据,通过选择放置通配符字符(由百分比符号 % 表示)的位置来选择。

select * from users where username like 'a%';

这将返回用户名以字母 a 开头的任何行。

select * from users where username like '%n';

这将返回用户名以字母 n 结尾的任何行。

select * from users where username like '%or%';

这将返回用户名中包含字母组合 or 的任何行。

UNION

UNION 语句用于组合两个或多个 SELECT 语句的结果集。结果集中的列数、列顺序和数据类型必须相同。这也是 SQL 攻击中的一种常见手法。

select username,password from users UNION select name,credit_card_number from customers;

在此查询中,我们尝试将两个表中的用户名和密码与客户表中的姓名和信用卡号组合在一起。这可能导致暴露敏感数据,因为攻击者可以通过 SQL 注入获取这些信息。

INSERT

INSERT 语句用于向表中插入新记录。以下是一个基本示例:

insert into users (username, password) values ('newuser', 'newpassword');

这将在名为 users 的表中插入一行,包含用户名为 newuser,密码为 newpassword。

UPDATE

UPDATE 语句用于更新表中的现有记录。以下是一个基本示例:

update users set password='newpassword' where username='existinguser';

这将更改名为 existinguser 的用户的密码为 newpassword。

DELETE

DELETE 语句用于从表中删除记录。以下是一个基本示例:

delete from users where username='user_to_delete';

这将从名为 users 的表中删除用户名为 user_to_delete 的行。

什么是 SQL 注入?

SQL 注入是一种通过将恶意 SQL 代码插入输入字段或 URI 中的查询字符串来攻击数据库的技术。这可能会导致攻击者能够访问或修改数据库中的敏感数据,绕过身份验证,甚至删除整个数据库。


以下面的场景为例,你遇到了一个在线博客,并且每个博客条目都有一个唯一的 ID 号。 博客条目可以设置为公开或私有,具体取决于它们是否准备好公开发布。 每个博客条目的 URL 可能如下所示:

https://website.thm/blog?id=1

从上面的 URL 中,你可以看到被选择的博客条目来自查询字符串中的 id 参数。 Web 应用程序需要从数据库检索文章,并且可能使用如下所示的 SQL 语句:

SELECT * from blog where id=1 and private=0 LIMIT 1;

让我们假设文章 ID 2 仍被锁定为私有,因此无法在网站上查看。 我们现在可以调用 URL:

https://website.thm/blog?id=2;--

然后,这将生成 SQL 语句:

SELECT * from blog where id=2;-- and private=0 LIMIT 1;

URL 中的分号表示 SQL 语句的结束,两个破折号会导致后面的所有内容都被视为注释。 通过这样做,你实际上只是运行查询:

SELECT * from blog where id=2;--

无论是否设置为公开,都会返回 id 为 2 的文章。

攻击类型

In-Band SQL 注入

这是最常见的 SQL 注入类型。攻击者使用与应用程序相同的通道从数据库检索数据。有两种主要的 In-Band 注入:

  • Union-Based SQL 注入: 攻击者使用 UNION 操作合并结果集,通过这种方式检索额外的信息。
select username, password from users where username = 'admin' union all select 1,2 from dual;

在这个例子中,'admin' 是正常的用户名,而 'union all select 1,2 from dual' 将返回一个额外的结果集,其中包含数字 1 和 2。

  • Error-Based SQL 注入: 攻击者故意触发错误,以便从数据库中检索有关其结构的信息。
select * from users where username = 'admin' and 1=convert(int, (select @@version));
 例子:

发现基于错误的 SQL 注入的关键是通过尝试某些字符来破坏代码的 SQL 查询,直到产生错误消息; 最常见的是单引号 (') 或双引号 (")。

假设你尝试在 id=1 后输入撇号 ( ' ),然后按 Enter 键。 返回一个 SQL 错误,通知你语法中存在错误。 你收到此错误消息这一事实证实了 SQL 注入漏洞的存在。 我们现在可以利用此漏洞并使用错误消息来了解有关数据库结构的更多信息。

我们需要做的第一件事是将数据返回到浏览器而不显示错误消息。 首先,我们将尝试 UNION 运算符,这样我们就可以获得我们选择的额外结果。 尝试将模拟浏览器 id 参数设置为:

1 UNION SELECT 1

此语句应该生成一条错误消息,通知你 UNION SELECT 语句的列数与原始 SELECT 查询的列数不同。 因此,让我们再试一次,但添加另一列:

1 UNION SELECT 1,2

再次出现同样的错误,所以让我们通过添加另一列来重复:

1 UNION SELECT 1,2,3

成功,错误消息消失了,并且正在显示文章,但现在我们想要显示我们的数据而不是文章。 正在显示该文章是因为它在网站代码中的某个位置获取第一个返回的结果并显示该结果。 为了解决这个问题,我们需要第一个查询不产生任何结果。 只需将文章 id 从 1 更改为 0 即可完成此操作。

0 UNION SELECT 1,2,3

现在,你将看到该文章仅由返回列值 1、2 和 3 的 UNION select 的结果组成。我们可以开始使用这些返回值来检索更多有用的信息。 首先,我们将获取我们有权访问的数据库名称:

0 UNION SELECT 1,2,database()

现在您将看到之前显示数字 3 的位置; 它现在显示数据库的名称,假设为sqli_one

我们的下一个查询将收集该数据库中的表的列表。

在此查询中有一些新内容需要学习。 首先, group_concat()从多个返回的行中获取指定的列(在我们的例子中为 table_name),并将其放入一个以逗号分隔的字符串中。 接下来是 information_schema数据库; 数据库的每个用户都可以访问它,并且它包含有关用户有权访问的所有数据库和表的信息。 在这个特定的查询中,我们感兴趣的是列出 sqli_one 数据库中的所有表。假设为article和staff_users。

如果我们的目标是发现 Martin 的密码,那么我们感兴趣的是 Staff_users 表。 我们可以再次利用 information_schema 数据库,使用以下查询查找该表的结构。

0 UNION SELECT 1,2,group_concat(column_name) FROM information_schema.columns WHERE table_name = 'staff_users'

查询结果为staff_users 表提供三列:id、password 和 username。 我们可以使用以下查询的用户名和密码列来检索用户的信息。

0 UNION SELECT 1,2,group_concat(用户名,':',密码 SEPARATOR '<br>') FROM Staff_users

我们再次使用 group_concat 方法将所有行返回到一个字符串中,并使其更易于阅读。 我们还添加了 ,':', 以将用户名和密码分开。 我们没有使用逗号分隔,而是选择了 HTML中的 <br> 标记,该标记强制每个结果位于单独的行上,以便于阅读。

盲注 SQLi - 认证绕过

盲注 SQLi

盲注 SQL 注入是当我们几乎没有反馈来确认我们注入的查询是否成功的情况,这是因为错误消息已被禁用,但注入仍然有效。

认证绕过

最直接的盲注 SQL 注入技术之一是绕过身份验证方法,如登录表单。在这种情况下,我们并不太关心从数据库中检索数据;我们只想通过登录。

与用户数据库连接的登录表单通常是这样开发的,即 Web 应用程序对用户名和密码的内容不感兴趣,而是更关心这两者是否在用户表中构成匹配对。简而言之,Web 应用程序向数据库询问“你是否有一个用户名为 bob,密码为 bob123 的用户?,数据库以是或否(真或假)回答,根据该答案决定是否允许 Web 应用程序让您继续。

考虑到上述信息,无需枚举有效的用户名/密码对。我们只需创建一个数据库查询,该查询以是/真作为回复。

例子:
select * from users where username='%username%' and password='%password%' LIMIT 1;

注意:**%username%** 和 **%password%** 的值取自登录表单字段,SQL 查询框中的初始值将为空,因为这些字段目前为空。

为了使其成为始终返回为真的查询,我们可以在密码字段中输入以下内容:

' OR 1=1;--

这将将 SQL 查询转换为以下形式:

select * from users where username='' and password='' OR 1=1;

由于 1=1 是一个真语句,并且我们使用了 OR 运算符,这将始终导致查询返回为真,从而满足 Web 应用程序的逻辑,即数据库找到了有效的用户名/密码组合,应该允许访问。

盲注 SQLi - 布尔型

布尔型

布尔型 SQL 注入是指我们从注入尝试中收到的响应,可能是真/假、是/否、打开/关闭、1/0或仅具有两个结果的任何响应。这个结果向我们确认我们的 SQL 注入有效与否。乍一看,您可能觉得这种有限的响应不能提供太多信息。但事实上,仅使用这两个响应,就可以枚举整个数据库结构和内容。

例子:


https://website.thm/checkuser?username=admin

浏览器主体包含 {"taken":true} 的内容。此 API 端点模拟了许多注册表单上常见的功能,检查用户名是否已注册,以提示用户选择不同的用户名。由于 taken 值设置为 true,我们可以假定用户名 admin 已注册。实际上,通过将模拟浏览器地址栏中的用户名从 admin 更改为 admin123 并按回车键,您将看到 taken 的值现在已更改为 false

处理的 SQL 查询如下:

select * from users where username = '%username%' LIMIT 1;

由于我们唯一有控制权的输入是查询字符串中的用户名,我们必须使用它来执行我们的 SQL 注入。将用户名保持为 admin123,我们可以开始附加到此以尝试使数据库确认真实的事物,这将将已占用的字段状态从 false 更改为 true。

与以前的级别一样,我们的第一个任务是确定用户表中的列数,这可以通过使用 UNION 语句来实现。将用户名值更改为以下内容:

admin123' UNION SELECT 1;--

由于 Web 应用程序以 taken 值为 false 响应,我们可以确认这是列数不正确的值。继续添加更多列,直到我们有 taken 值为 true。您可以通过将用户名设置为以下值来确认答案为三列:

admin123' UNION SELECT 1,2,3;-- 

现在,我们已经确定了列数,可以开始枚举数据库。我们的第一个任务是发现数据库名称。我们可以通过使用内置的 database() 方法,然后使用 like 运算符来尝试找到将返回 true 状态的结果。

admin123' UNION SELECT 1,2,3 where database() like '%';--

我们得到一个真回应,因为在 like 运算符中,我们只有 % 的值,它将匹配任何内容,因为它是通配符值。如果我们将通配符运算符更改为 a%,您将看到响应变为 false,从而确认数据库名称不以字母 a 开头。我们可以循环浏览所有字母、数字和字符,例如 - 和 _,直到找到匹配。如果将以下内容发送为用户名值,您将收到一个 true 响应,确认数据库名称以字母 s 开头

admin123' UNION SELECT 1,2,3 where database() like 's%';--

现在,您可以继续到数据库名称的下一个字符,直到找到另一个 true 响应,例如 sa%、sb%、sc% 等。继续这个过程,直到发现数据库名称的所有字符,即 sqli_three

我们已经确认了数据库名称,现在可以使用它来使用类似的方法枚举表名,利用 information_schema 数据库中的信息。尝试将用户名设置为以下值:

admin123' UNION SELECT 1,2,3 FROMinformation_schema.tables WHERE table_schema = 'sqli_three' and table_name like 'a%';--

此查询在 information_schema 数据库的 tables 表中寻找结果,其中数据库名称匹配 sqli_three,表名以字母 a 开头。由于上述查询导致 false 响应,我们可以确认 sqli_three 数据库中没有以字母 a 开头的表。与以前一样,您将需要循环浏览字母、数字和字符,直到找到积极匹配。

最后,您最终会发现 sqli_three 数据库中有一个名为 users 的表,您可以通过运行以下用户名负载来确认:

admin123' UNION SELECT 1,2,3 FROM information_schema.tables WHERE table_schema = 'sqli_three' and table_name='users';--

最后,我们现在需要枚举 users 表中的列名,以便我们可以正确搜索其中的登录凭据。再次使用 information_schema 数据库和我们已经获得的信息,我们可以开始查询列名。使用下面的负载,我们在数据库等于 sqli_three、表名等于 users 且列名以字母 a 开头的地方搜索 columns 表。

admin123' UNION SELECT 1,2,3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='sqli_three' and TABLE_NAME='users' and COLUMN_NAME like 'a%';

再次循环浏览字母、数字和字符,直到找到匹配。由于您正在寻找多个结果,因此每次找到新的列名时,都必须将其添加到您的负载中,以免继续发现相同的列。例如,一旦找到名为 id 的列,你将将其追加到原始负载中(如下所示)。

admin123' UNION SELECT 1,2,3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='sqli_three' and TABLE_NAME='users' and COLUMN_NAME like 'a%' and COLUMN_NAME !='id';

重复此过程三次将使您能够发现列 id、username 和 password。现在,您可以使用这些列在 users 表中查询登录凭据。首先,您需要发现一个有效的用户名,可以使用以下负载:

admin123' UNION SELECT 1,2,3 from users where username like 'a%

通过循环浏览所有字符,您将确认用户名 admin 的存在。现在您已经有了用户名,可以专注于发现密码。下面的负载显示了如何查找密码:

admin123' UNION SELECT 1,2,3 from users where username='admin' and password like 'a%

通过循环浏览所有字符,您将发现密码为 3845。

盲注 SQLi - 基于时间的

基于时间的盲注 SQL 注入与上述布尔型相似,因为发送的是相同的请求,但这次没有视觉指示您的查询是否正确或错误。相反,正确查询的指示是基于查询完成的时间。通过使用内置方法,如 SLEEP(x) 与 UNION 语句一起,引入时间延迟。SLEEP() 方法只会在成功的 UNION SELECT 语句中执行。

例子:
admin123' UNION SELECT SLEEP(5);--

如果响应时间没有暂停,我们知道查询失败了,因此与之前的任务一样,我们添加另一列:

admin123' UNION SELECT SLEEP(5),2;--

此负载应生成 5 秒的延迟,确认 UNION 语句的成功执行以及存在两列。

现在,你可以从基于布尔的 SQL 注入的枚举过程中重复此操作。

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

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

相关文章

LeetCode每日一题 | 2808. 使循环数组所有元素相等的最少秒数

文章目录 题目描述问题分析程序代码 题目描述 原题链接 给你一个下标从 0 开始长度为 n 的数组 nums 。 每一秒&#xff0c;你可以对数组执行以下操作&#xff1a; 对于范围在[0, n - 1]内的每一个下标i&#xff0c;将nums[i]替换成nums[i]&#xff0c;nums[(i - 1 n) % n]或…

自定义vue通用左侧菜单组件(未完善版本)

使用到的技术&#xff1a; vue3、pinia、view-ui-plus 实现的功能&#xff1a; 传入一个菜单数组数据&#xff0c;自动生成一个左侧菜单栏。菜单栏可以添加、删除、展开、重命名&#xff0c;拖动插入位置等。 效果预览&#xff1a; 代码&#xff1a; c-menu-wrap.vue <t…

docker swarm转移K8s 实践(-)

相关资料 Kubernetes初识_kubernetes怎么读-CSDN博客

【Linux】压缩脚本、报警脚本

一、压缩搅拌 要求&#xff1a; 写一个脚本&#xff0c;完成如下功能 传递一个参数给脚本&#xff0c;此参数为gzip、bzip2或者xz三者之一&#xff1b; (1) 如果参数1的值为gzip&#xff0c;则使用tar和gzip归档压缩/etc目录至/backups目录中&#xff0c;并命名为/backups/etc…

鸿蒙OS之Rust开发

背景 Rust是一门静态强类型语言&#xff0c;具有更安全的内存管理、更好的运行性能、原生支持多线程开发等优势。Rust官方也使用Cargo工具来专门为Rust代码创建工程和构建编译。 OpenHarmony为了集成C/C 代码和提升编译速度&#xff0c;使用了GN Ninja的编译构建系统。GN的构…

Open CASCADE学习|遍历曲面的边

目录 1、球面的Brep数据 2、C遍历球面的边 ​这里以球面为例来说明如何遍历曲面的边。 1、球面的Brep数据 使用Tcl命令在Draw Test Harness中生成的球面并到出Brep数据如下&#xff1a; pload ALL psphere asphere 1 dump asphere 结果如下&#xff1a; *********** D…

构建高效外卖系统:利用Spring Boot框架实现

在当今快节奏的生活中&#xff0c;外卖系统已经成为人们生活中不可或缺的一部分。为了构建一个高效、可靠的外卖系统&#xff0c;我们可以利用Spring Boot框架来实现。本文将介绍如何利用Spring Boot框架构建一个简单但功能完善的外卖系统&#xff0c;并提供相关的技术代码示例…

C++ std::thread 的基本使用方法Linux强制结束进程

std::thread 是 C11 中的一个多线程库&#xff0c;用于创建和管理线程。使用 std::thread&#xff0c;可以将一个函数或可调用对象作为参数&#xff0c;创建一个新的线程来运行该函数或对象。 下面是 std::thread 的基本用法&#xff1a; 包含头文件#include < thread >…

Qt SQLite3数据库加密 QtCipherSqlitePlugin

在客户端软件开发过程中&#xff0c;基本都会涉及到数据库的开发。QT支持的数据库也有好几种&#xff08;QSQLITE, QODBC, QODBC3, QPSQL, QPSQL7&#xff09;&#xff0c;SQLite就是其中之一&#xff0c;但这个 SQLite 是官方提供的开源版本&#xff0c;没有加密功能的。如果对…

ElasticSearch 8.x 版本如何使用 SearchRequestBuilder 检索

ElasticSearch 1、ElasticSearch学习随笔之基础介绍 2、ElasticSearch学习随笔之简单操作 3、ElasticSearch学习随笔之java api 操作 4、ElasticSearch学习随笔之SpringBoot Starter 操作 5、ElasticSearch学习随笔之嵌套操作 6、ElasticSearch学习随笔之分词算法 7、ElasticS…

安全小记-sqli-labs闯关

1.安装靶场 介绍&#xff1a; SQLI&#xff0c;sql injection&#xff0c;我们称之为sql注入。何为sql&#xff0c;英文&#xff1a;Structured Query Language&#xff0c;叫做结构化查询语言。常见的结构化数据库有MySQL&#xff0c;MS SQL ,Oracle以及Postgresql。Sql语言…

Python笔记16-实战小游戏飞机大战(下)

文章目录 play按钮重置游戏提高等级游戏完成 我们会添加一个Play按钮&#xff0c;用于根据需要启动游戏以及在游戏结束后重启游戏&#xff0c;还会修改这个游戏&#xff0c;使其随玩家等级提高而加快节奏。 play按钮 添加一个Play按钮&#xff0c;它在游戏开始前出现&#xff…

【Spark系列4】Task的执行

一、Task的执行流程 1.1、Task执行流程 DAGScheduler将Stage生成TaskSet之后&#xff0c;会将Task交给TaskScheduler进行处理&#xff0c;TaskScheduler负责将Task提交到集群中运行&#xff0c;并负责失败重试&#xff0c;为DAGScheduler返回事件信息等&#xff0c;整体如流程…

OpenGL ES 渲染 NV21、NV12 格式图像有哪些“姿势”?

使用2个纹理实现 NV21 格式图像渲染 前文提到渲染 NV21 格式图像需要使用 2 个纹理,分别用于保存 Y plane 和 UV plane 的数据,然后在片段着色器中分别对 2 个纹理进行采样,转换成 RGB 数据。 OpenGLES 渲染 NV21或 NV12 格式图像需要用到 GL_LUMINANCE 和 GL_LUMINANCE_A…

http和https的区别是什么?https有什么优缺点?

HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一个简单的请求-响应协议&#xff0c;它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。这个简单模型是早期Web成功的有功之臣&#xff0c;因为它…

The following untracked working tree files would be overwritten by merge问题的解决

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> 在更新git仓库时出现了一个The following untracked working tree files would be overwritten by merge的错误&#xff0c;具体如下图&#xff1a; 分析…

windows下安装Linux虚拟机

一、 一般情况使用VMware虚拟机&#xff08;个人习惯&#xff0c;也可以使用红帽&#xff09; 下载VMware Workstation 虚拟机&#xff0c; 网址&#xff1a;https://vmware-workstation.en.softonic.com/ 二、 下载CentOS镜像文件&#xff0c; 网址&#xff1a;https://ww…

ES 分词器

概述 分词器的主要作用将用户输入的一段文本&#xff0c;按照一定逻辑&#xff0c;分析成多个词语的一种工具 什么是分词器 顾名思义&#xff0c;文本分析就是把全文本转换成一系列单词&#xff08;term/token&#xff09;的过程&#xff0c;也叫分词。在 ES 中&#xff0c;Ana…

深入理解vue相关的底层原理

keep-alive 组件的作用及原理 keep-alive 是 Vue 提供的一个内置组件&#xff0c;在组件切换过程中将状态保留在内存中&#xff0c;防止重复渲染 DOM。 如果为一个组件包裹了 keep-alive&#xff0c;那么它会多出两个生命周期&#xff1a;deactivated、activated。同时&#…

代码随想录算法训练营Day38|动态规划理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

目录 动态规划理论基础 什么是动态规划 动态规划的解题步骤 动态规划的debug 509. 斐波那契数 前言 思路 算法实现 方法一&#xff1a;动态规划 方法二&#xff1a;递归法 70. 爬楼梯 前言 思路 算法实现 拓展 746. 使用最小花费爬楼梯 算法实现 总结 动态规划…