【玩转 Postman 接口测试与开发2_018】第14章:利用 Postman 初探 API 安全测试
《API Testing and Development with Postman》最新第二版封面
文章目录
- 第十四章 API 安全测试
- 1 OWASP API 安全清单
- 1.1 相关背景
- 1.2 OWASP API 安全清单
- 1.3 认证与授权
- 1.4 破防的对象级授权(Broken object-level authorization)
- 1.5 破防的属性级授权(Broken property-level authorization)
- 1.6 不受控制的资源消耗(Unrestricted resource consumption)
- 1.7 不受限制地访问业务流(Unrestricted access to business workflows)
- 1.8 不安全地使用 API 接口(Unsafe consumption of APIs)
- 2 模糊测试(Fuzzy)
- 2.1 相关概念
- 2.2 实战:在 Postman 中执行模糊测试
- 2.3 绕开 Collection Runner 的测试方案
- 3 利用 Postman 内置随机变量实现模糊测试
- 4 小结
写在前面
本章为全书的倒数第二章,作者简要介绍了 API 安全测试的相关概念,并对 OWASP API 安全清单和模糊测试(Fuzzy Testing)进行了重点讲解;后半部分作者利用数据驱动测试演示了模糊测试在 Postman 中的具体应用,只可惜在实现方案和叙述的条理性上较为敷衍,导致我在实测过程中又踩了不少坑。特此梳理出来,既是对自己创新方案的复盘,也能让更多后来者少走弯路。
第十四章 API 安全测试
本章概要
OWASP
API 安全清单- 用
Postman
进行模糊测试(Fuzz testing
)的方法
1 OWASP API 安全清单
1.1 相关背景
- OWASP(Open Web Application Security Project)是一个非营利组织,专注于提高软件安全性;
- 提供免费、开放的资源,如工具、文档、论坛等,帮助开发者和安全人员构建安全的应用程序;
- 知名项目包括 OWASP Top 10(十大 Web 应用安全风险)和 API Security Top 10(API安全十大风险);
- 相关资源:
- 网站门户:https://owasp.org/;
- 2023 最新版 API 安全清单:https://owasp.org/www-project-api-security/;
1.2 OWASP API 安全清单
- API Security Top 10 是 OWASP 针对 API 安全的核心文档,罗列了 API 面临的十大安全风险。
- 适用于开发者、安全工程师和架构师,帮助识别和缓解API安全威胁。
1.3 认证与授权
黑客最先尝试的攻击方式是身份验证,即通过用户名密码攻击。
最简单的方式是暴力破解(brute-force attack),因此 API 接口应采取在一定时间段内 限制登录次数 的防护措施。
具体实现:以 GitPod
演示项目为例,可利用 Postman
内置的随机数据变量高频多次调用登录接口(多次手动点击或 Collection Runner
设置迭代次数):
测试脚本:
for (let i = 0, len = 50; i < len; i++) {pm.sendRequest({url: pm.variables.replaceIn('{{base_url}}/token'),method: 'POST',header: { 'Content-Type': 'multipart/form-data'},body: {mode: "formdata",formdata: [{ key: 'username', value: pm.variables.replaceIn('{{$randomUserName}}') },{ key: 'password', value: pm.variables.replaceIn('{{$randomPassword}}') }]}},function (err, resp) {if (err) {console.error(JSON.parse(err));return;}pm.test(`${i + 1}: Response JSON have detail attribute`, function () {pm.expect(resp.json()).to.eql({ "detail": "Incorrect user name or password" });});})
}
实测结果(鉴权接口疑似不具备限制登录次数功能):
【图 14.1 构造不同的用户名和密码,多次高频调用登录接口,模拟暴力破解过程】
1.4 破防的对象级授权(Broken object-level authorization)
这是 2023 年十大安全清单排名第一的高风险议题:
- 问题描述:未正确验证用户对对象的访问权限,导致越权访问。
- 应对措施:实施严格的权限验证,确保用户只能访问授权资源。
- 示例:用登录普通用户(
user1/12345
)的登录令牌去访问管理员帐号,看看示例项目是否会响应 403 错误。
实测结果:
【图 14.2 用 user1 的登录令牌访问管理员 admin 的帐号信息,后台报 403 错误(符合预期)】
1.5 破防的属性级授权(Broken property-level authorization)
示例:用 user1/12345
登录,先用 "user1"
为创建人(即 create_by
字段)添加一个正常的待办项,然后看看是否可以通过 PUT
请求篡改该创建人信息(例如改为 "user2"
)。
测试脚本:
// PUT request's Post-response
pm.collectionVariables.set('reqPut', pm.request);// Pre-request
pm.sendRequest(pm.collectionVariables.get('reqPut'),function(err, resp) {if(err) {console.error('Updated failure:', err);return;}pm.test('PUT req: status code should be 200', () => {console.log(resp)pm.expect(resp.code).to.eq(200);});}
)// Post-response
pm.test('The creator should not be updated (user1) after running PUT req',() => {const [{ created_by: creator }] = pm.response.json();pm.expect(creator).to.eql('user1')});
实测结果:
【图 14.3 实测属性级授权漏洞(篡改失败,符合预期)】
但实测发现,在创建待办项时人为设置创建人为 非当前登录用户(如 "user2"
),最后仍然创建成功了,说明该接口还是有问题的:
【图 14.4 创建任务时篡改创建人信息,最终任务创建成功,说明该接口仍然支持篡改信息】
1.6 不受控制的资源消耗(Unrestricted resource consumption)
方式一:通过耗尽所有系统资源来损害系统,导致系统瘫痪(denial-of-service attacks,拒绝服务攻击);
方式二:由于未对接口调用次数进行限制,大量请求将拖慢响应速度;如果接口调用了第三方付费资源,还会产生大量费用。
1.7 不受限制地访问业务流(Unrestricted access to business workflows)
攻击者可能会利用抓包、监控请求数据等方式获取系统内部通信 API,从而获悉内部工作流结构,给系统带来严重威胁(例如恶意逃票等)。
应对措施:仔细考虑暴露了哪些接口;严格控制接口访问权限。
1.8 不安全地使用 API 接口(Unsafe consumption of APIs)
典型案例:引用了被黑客攻击的第三方 API。
这类测试较为棘手,可能需要搭建一个模拟服务器,让从第三方 API 响应的数据在该模拟器上先行缓冲,或者模拟危险数据进行测试,看看本地后台是否能够鉴别该类数据。
2 模糊测试(Fuzzy)
2.1 相关概念
定义:模糊测试(Fuzzy) 是一种通过向程序提供无效、随机或意外的输入来发现漏洞和错误的测试技术。
主要特征:
- 非通用测试技术
- 有助于发现未考虑到、或未测试到的问题
具体的运行方式:
- 手动执行:通过在 UI 中输入伪随机数据来完成(极少);
- 编程执行:以程序的形式运行(绝大多数)。工具如
PeachFuzzer
或自定义输入集。
输入的生成:
- 生成大量随机或半随机数据,可能包含格式错误或危险字符(如转义字符、引号等)。
- 输入通常是随机的,但有一些边界限制(例如,字节 vs ASCII/Unicode 字符串)。
- 输入可以偏向已知的“危险”字符(例如转义字符、引号等)。
输入内容的具体方式:
- 通过命令行参数注入
- 通过文件输入
- 通过网络协议
- 通过 API 输入(特别适合模糊测试,易于用程序控制)
模糊查询的应用场景:
- 安全测试场景:揭示未能预见的攻击方向,发现错误处理机制中的薄弱环节等;
- API 测试场景:具有易于访问且数量巨大的测试输入,尤其适合 API 测试。
2.2 实战:在 Postman 中执行模糊测试
以 GitHub
开源项目 Big List of Naughty Strings 的 JSON
文件为数据源,利用 Collection Runner
对 GitPod
演示项目 ToDo List App
进行基于数据驱动的模糊测试,看看 POST /tasks
接口在大量随机输入下的响应情况。
首先从开源项目下载原始数据文件 blns.base64.json
。该原始数据为 Base64
编码的字符串数组:
["", "dW5kZWZpbmVk", "dW5kZWY=", "bnVsbA==", ...
]
使用前需要先处理成如下格式(可使用 vim
宏的批量操作完成):
[{"naughtyString":""}, {"naughtyString":"dW5kZWZpbmVk"}, {"naughtyString":"dW5kZWY="}, {"naughtyString":"bnVsbA=="}, ...
]
然后创建测试集合 FuzzyTest
以及 POST
请求 Create a task
,其 URL
设置为 {{base_url}}/tasks
,其中 base_url
为集合变量,其值为 GitPod
演示项目的基础 URL
(启动链接:https://gitpod.io/new/#https://github.com/djwester/todo-list-testing)。
接着,在测试请求的请求体中输入如下内容(这里有个大坑,后面会讲):
{"description": "{{naughtyString}}","status": "Draft"
}
上述代码中的 naughtyString
为数据驱动测试启动后、经 Pre-request
脚本处理得到的动态变量:
const atob = require('atob');
const encoded_string = pm.iterationData.get("naughtyString");
pm.collectionVariables.set('naughtyString', atob(encoded_string));
对应的 Post-response
响应后脚本如下:
pm.test("Status code is 201", function() {pm.response.to.have.status(201);
});
一切准备就绪后,启动 Collection Runner
,加载 JSON
映射文件,执行模糊测试:
【图 14.5 利用 Collection Runner,发起基于JSON 文件数据驱动的模糊测试】
由于是分别读取每行数据并调用创建接口,整个过程需要多等些时间,最终得到结果:
【图 14.6 Collection Runner 运行结束后的实测截图】
由于作者演示时用的是本地部署的 ToDo List App
,全套数据测完只用了 1'14"
,和我实测时的 23'56"
真是天壤之别(没办法,Python 基础有限,几次尝试本地部署都失败了)。但奇怪的是,除了 10 个请求因为网络原因发送失败,其余 666 个都成功了,并没有报错。
可高兴不到一分钟,我就知道出问题了。作者并没有交代要用 user2
登录,而我新增任务前压根儿就没登录,导致后续的数据清空全部失败了:
【图 14.7 由于新增任务时没有登录,导致后续的批量删除全部失败(神坑)】
没办法,只能重置项目,重新来过……
先 Ctrl + C 中断 GitPod
项目,运行重置数据命令 poetry run python remove_tables.py
,然后执行 make run-dev
重新启动。
这次先用 user2/12345
换取登录令牌,再到新增接口中完成鉴权:
【图 14.8 用 user2 的登录令牌完成新增接口的鉴权设置】
然后只选取新增接口,再次上传 JSON
数据集运行 Collection Runner
:
【图 14.9 重新运行 Collection Runner 的配置界面截图】
也不知道是不是这份死磕精神感动了马克思,第二次运行居然全部成功了:
【图 14.10 第二次运行结果截图(676 条数据全部新增成功)】
接着在浏览器查看项目首页,也没有书中说的 alert
注入问题(当真欧皇附体?):
借着这波好运,赶紧再跑一遍数据批量清空。在 GET {{base_url}}/task
接口的响应后脚本中输入以下内容(直接用 JS 脚本批量删除,书中方法太过陈旧,还得再消耗一次 Collection Runner
免费额度,不知道作者怎么想的):
const tasks = pm.response.json();
const task_ids = tasks.map(t => t.id);const base_url = pm.collectionVariables.get('base_url');
const auth = {type: 'bearer',bearer: [{key: 'token',value: pm.collectionVariables.replaceIn('{{token}}'),type: 'string'}]
};
const getCallback = task_id => (err, response) => {if(err) {console.error(err);return;}pm.test(`Deleting id(${task_id}): Status should be OK`, function() {pm.expect(response.status).to.eql('OK');});
};for(const id of task_ids) {// Delete the task by idpm.sendRequest({url: `${base_url}/tasks/${id}`,method: 'DELETE',auth}, getCallback(id));
}
结果还是报错:
仔细一想,真相大白了:新增接口必须在请求体中手动指定 created_by
字段,否则 一律按匿名处理(就当是作者故意挖的坑吧)。
只有再重来一遍了:
再次执行批量删除,只有一次是后台原因删除失败,五次请求未发送,其余全部删除成功,终于熬出头了:
2.3 绕开 Collection Runner 的测试方案
其实只要具备 JavaScript
基础,完全可以跳过 Collection Runner
的限制,在 Pre-request
/ Post-response
脚本中实现批量新增和删除。
批量新增的纯 JS 脚本实现:
-
将原始
JSON
数据集不经任何处理直接放到Postman
的私有模块中,例如my-blns
:const data = ["", "dW5kZWZpbmVk", "dW5kZWY=", "bnVsbA==", "TlVMTA==", "KG51bGwp", // ..."e3sgIiIuX19jbGFzc19fLl9fbXJvX19bMl0uX19zdWJjbGFzc2VzX18oKVs0MF0oIi9ldGMvcGFz","c3dkIikucmVhZCgpIH19" ]; module.exports = {data };
-
新建请求
GET {{base_url}}/tasks
,用于在批量新增后查询总的待办项列表:- 在
Pre-request
中输入以下脚本:
const atob = require('atob'); const { data } = pm.require('your/package/path/to/my-blns'); // console.log(data.length);const postRequest = description => ({url: pm.collectionVariables.replaceIn('{{base_url}}/tasks'),method: 'POST',header: { 'Content-Type': 'application/json' },body: {mode: 'raw',raw: JSON.stringify({description,status: "Draft",created_by: "user2"})} });data.map(d => atob(d)).map((description, i) => pm.sendRequest(postRequest(description), (err, resp) => {if(err) {console.error(err);return;}pm.test(`Create task_${i+1} completed: the status code should be 201`,() => pm.expect(resp.code).to.eql(201));}));
-
在
Post-response
输入以下脚本:pm.test('Task list length should be greater than 0', function () {pm.expect(pm.response.json()).to.be.an('array').and.to.have.lengthOf.at.least(1, "Task list length should be greater than 0"); });
- 在
-
至于批量删除,刚才实测过程中已经看过脚本了,这里只说明一下实现逻辑。利用列表查询接口
GET {{base_url}}/tasks
获取到任务列表后,批量提取任务列表的id
;然后分别调用DELETE
接口POST {{base_url}}/tasks/:id
完成删除(注意:删除待办项时,别忘了在请求中带上鉴权配置对象auth
)。
这样就可以在 Postman
中随意批量新增和删除待办任务了,完全不受 Collection Runner
的制约。
3 利用 Postman 内置随机变量实现模糊测试
其实就是将上传的 JSON
文件内容用 Postman
内置的各种随机变量实现同样的模糊测试效果,例如将新增接口请求体中的 JSON
改为:
{"description": "{{$randomLoremSentence}}","status": "Draft","created_by": "user2"
}
或者借助测试脚本,引入 lodash
实现更多模糊测试效果:
const _ = require('lodash');
// _.sample(collection): Gets a random element from collection.
const description = _.sample(["{{$randomAbbreviation}}", "{{$randomAdjective}}"]);
const jsonBody = {description,"status", "Draft","created_by": "user2"
}
4 小结
本章内容整体感觉较敷衍,上半部分介绍相关概念几乎全是蜻蜓点水式的描述,后半段实战环节的条理性又明显不如前面的章节,漏掉很多关键细节,且在方案选择上过分依赖 Collection Runner
,致使我在实测过程中浪费了不少免费额度。不过依次填完这些坑后,我也有机会用纯 JavaScript
脚本的方式实现待办任务的批量创建与删除,顺便学习了用 pm.sendRequest()
发送 POST
请求和 DELETE
请求的写法,也算是因祸得福了吧。
后记
最后再跟大家分享个操作技巧,遇到不会写的Postman
脚本,可以利用其自带的 AI 机器人Postbot
直接给答案。本章实测中几种pm.sendRequest()
的写法就是这么来的,比查官方文档快多了。大家一定要学会使用 AI 工具,千万不要故步自封,成为 AI 时代的 “新文盲”。
相关文章:

【玩转 Postman 接口测试与开发2_018】第14章:利用 Postman 初探 API 安全测试
《API Testing and Development with Postman》最新第二版封面 文章目录 第十四章 API 安全测试1 OWASP API 安全清单1.1 相关背景1.2 OWASP API 安全清单1.3 认证与授权1.4 破防的对象级授权(Broken object-level authorization)1.5 破防的属性级授权&a…...

如何编写测试用例
代码质量管理是软件开发过程中的关键组成部分,比如我们常说的代码规范、代码可读性、单元测试和测试覆盖率等,对于研发人员来说单元测试和测试覆盖率是保障自己所编写代码的质量的重要手段;好的用例可以帮助研发人员确保代码质量和稳定性、减…...

复原IP地址(力扣93)
有了上一道题分割字符串的基础,这道题理解起来就会容易很多。相同的思想我就不再赘述,在这里我就说明一下此题额外需要注意的点。首先是终止条件如何确定,上一题我们递归到超过字符串长度时,则说明字符串已经分割完毕,…...

zzcms接口index.php id参数存在SQL注入漏洞
zzcms接口index.php id参数存在SQL注入漏洞 漏洞描述 ZZCMS 2023中发现了一个严重漏洞。该漏洞影响了文件/index.php中的某些未知功能,操纵参数id会导致SQL注入,攻击可能是远程发起的,该漏洞已被公开披露并可被利用。攻击者可通过sql盲注等手段,获取数据库信息。 威胁等级:…...

Redis03 - 高可用
Redis高可用 文章目录 Redis高可用一:主从复制 & 读写分离1:主从复制的作用2:主从复制原理2.1:全量复制2.2:增量复制(环形缓冲区) 3:主从复制实际演示3.1:基本流程准…...

系统URL整合系列视频四(需求介绍补充)
视频 系统URL整合系列视频四(需求补充说明) 视频介绍 (全国)大型分布式系统Web资源URL整合需求(补充)讲解。当今社会各行各业对软件系统的web资源访问权限控制越来越严格,控制粒度也越来越细。…...

激活函数篇 03 —— ReLU、LeakyReLU、ELU
本篇文章收录于专栏【机器学习】 以下是激活函数系列的相关的所有内容: 一文搞懂激活函数在神经网络中的关键作用 逻辑回归:Sigmoid函数在分类问题中的应用 整流线性单位函数(Rectified Linear Unit, ReLU),又称修正线性单元&a…...

山东大学软件学院人机交互期末复习笔记
文章目录 2022-2023 数媒方向2023-2024 软工方向重点题目绪论发展阶段 感知和认知基础视觉听觉肤觉知觉认知过程和交互设计原则感知和识别注意记忆问题解决语言处理影响认知的因素 立体显示技术及其应用红蓝眼镜偏振式眼镜主动式(快门时)立体眼镜 交互设…...

python 语音识别方案对比
目录 一、语音识别 二、代码实践 2.1 使用vosk三方库 2.2 使用SpeechRecognition 2.3 使用Whisper 一、语音识别 今天识别了别人做的这个app,觉得虽然是个日记app 但是用来学英语也挺好的,能进行语音识别,然后矫正语法,自己说的时候 ,实在不知道怎么说可以先乱说,然…...

docker常用命令及案例
以下是 Docker 的所有常用命令及其案例说明,按功能分类整理: 1. 镜像管理 1.1 拉取镜像 命令: docker pull <镜像名>:<标签>案例: 拉取官方的 nginx 镜像docker pull nginx:latest1.2 列出本地镜像 命令: docker images案例: 查看本地所有…...

DeepSeek-R1 云环境搭建部署流程
DeepSeek横空出世,在国际AI圈备受关注,作为个人开发者,AI的应用可以有效地提高个人开发效率。除此之外,DeepSeek的思考过程、思考能力是开放的,这对我们对结果调优有很好的帮助效果。 DeepSeek是一个基于人工智能技术…...

Java_双列集合
双列集合特点 存放的是键值对对象(Entry) Map 因为都是继承Map,所以要学会这些API,后面的类就都知道了 put 有两个操作,添加(并返回null)或者覆盖(返回被覆盖的值)…...

.net的一些知识点6
1.写个Lazy<T>的单例模式 public class SingleInstance{private static readonly Lazy<SingleInstance> instance new Lazy<SingleInstance>(() > new SingleInstance());private SingleInstance(){}public static SingleInstance Instace > instance…...

无须付费,安装即是完全版!
不知道大家有没有遇到过不小心删掉了电脑上超重要的文件,然后急得像热锅上的蚂蚁? 别担心,今天给大家带来一款超给力的数据恢复软件,简直就是拯救文件的“救星”! 数据恢复 专业的恢复数据软件 这款软件的界面设计得特…...

常见数据库对象与视图VIEW
常见的数据库对象 表 TABLE 数据字典 约束 CONSTRAINT 视图 VIEW 索引 INDEX 存储过程 PROCESS 存储函数 FUNCTION 触发器 TRIGGER 视图VIEW 1、引入 为什么使用视图? 视图可以帮助我们使用表的一部分,针对不同的用户制定不同的查询视图。 …...

【Vue2】vue2项目中如何使用mavon-editor编辑器,数据如何回显到网页,如何回显到编辑器二次编辑
参考网站: 安装使用参考:vue2-常用富文本编辑器使用介绍 html网页展示、编辑器回显二次编辑参考:快速搞懂前端项目如何集成Markdown插件mavon-editor,并回显数据到网页 安装命令 npm install mavon-editor2.9.1 --save全局配置 …...

2、Python面试题解析:如何进行字符串插值?
Python字符串插值详解 字符串插值是将变量或表达式嵌入字符串中的一种技术,Python提供了多种方式实现字符串插值。以下是常见的几种方法及其详细解析和代码示例。 1. 百分号(%)格式化 这是Python早期版本中的字符串插值方法,类似…...

计算机网络-SSH基本原理
最近年底都在忙,然后这两天好点抽空更新一下。前面基本把常见的VPN都学习了一遍,后面的内容应该又继续深入一点。 一、SSH简介 SSH(Secure Shell,安全外壳协议)是一种用于在不安全网络上进行安全远程登录和实现其他安…...

doris:MySQL 兼容性
Doris 高度兼容 MySQL 语法,支持标准 SQL。但是 Doris 与 MySQL 还是有很多不同的地方,下面给出了它们的差异点介绍。 数据类型 数字类型 类型MySQLDorisBoolean- 支持 - 范围:0 代表 false,1 代表 true- 支持 - 关键字&am…...

mysql 存储过程和自定义函数 详解
首先创建存储过程或者自定义函数时,都要使用use database 切换到目标数据库,因为存储过程和自定义函数都是属于某个数据库的。 存储过程是一种预编译的 SQL 代码集合,封装在数据库对象中。以下是一些常见的存储过程的关键字: 存…...

C++ 中的 cJSON 解析库:用法、实现及递归解析算法与内存高效管理
在现代软件开发中,JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,因其易于阅读和编写、易于机器解析和生成的特性,被广泛应用于各种场景。C 作为一种强大的编程语言,自然也需要一个高效的…...

websocket自动重连封装
websocket自动重连封装 前端代码封装 import { ref, onUnmounted } from vue;interface WebSocketOptions {url: string;protocols?: string | string[];reconnectTimeout?: number; }class WebSocketService {private ws: WebSocket | null null;private callbacks: { [k…...

【C语言】球球大作战游戏
目录 1. 前期准备 2. 玩家操作 3. 生成地图 4. 敌人移动 5. 吃掉小球 6. 完整代码 1. 前期准备 游戏设定:小球的位置、小球的半径、以及小球的颜色 这里我们可以用一个结构体数组来存放这些要素,以方便初始化小球的信息。 struct Ball {int x;int y;float r;DWORD c…...

人工智能D* Lite 算法-动态障碍物处理、多步预测和启发式函数优化
在智能驾驶领域,D* Lite 算法是一种高效的动态路径规划算法,适用于处理环境变化时的路径重规划问题。以下将为你展示 D* Lite 算法的高级用法,包含动态障碍物处理、多步预测和启发式函数优化等方面的代码实现。 代码实现 import heapq impo…...

MySQL 8版本认证问题
目录 问题: Public Key Retrieval is not allowed原因: mysql 8.0 调整身份认证机制解决方法(三种) 问题: Public Key Retrieval is not allowed 连接MySQL8数据库的时候,报错内容如下:“Publi…...

Android 开发APP中参数配置与读取总结
以使用MQTT配置的参数 MQTT_BROKER_UR 、MQTT_USER_NAME、 MQTT_PASSWORD为例,说明配置设置和读取应用 项目中使用系统参数(如环境变量和gradle.properties文件中的属性)在Gradle构建脚本中,以下是一个详细的操作文档资料&…...

Scala 语法入门
Scala语法入门 1. 定义变量2. 定义方法3. 闭包4. 声明字符串5. 声明数组6. 声明集合7. 异常处理 1. 定义变量 (变量的类型在变量名之后等号之前声明) 不可变变量(val) 类似于 Java 中的 final 变量,即一旦赋值后,其值不能再被改…...

python中的flask框架
Flask 是一个用Python编写的轻量级Web应用框架 基于WSGI和Jinja2模板引擎 被称为“微框架”,其核心功能简单,不捆绑数据库管理、表单验证等功能,而是通过扩展来增加其他功能 Flask提供最基本的功能,不强制使用特定工具或库 通…...

【redis】缓存设计规范
本文是 Redis 键值设计的 14 个核心规范与最佳实践,按重要程度分层说明: 一、通用数据类型选择 这里我们先给出常规的选择路径图。 以下是对每个步骤的分析: 是否需要排序?: zset(有序集合)用…...

归一化与伪彩:LabVIEW图像处理的区别
在LabVIEW的图像处理领域,归一化(Normalization)和伪彩(Pseudo-coloring)是两个不同的概念,虽然它们都涉及图像像素值的调整,但目的和实现方式截然不同。归一化用于调整像素值的范围,…...