邮件验证码存储推荐方式
邮件验证码的存储方案需要兼顾 安全性、性能 和 可维护性,以下是详细分析和推荐方案:
1. 推荐方案:Redis(首选)
为什么选择 Redis?
优势 | 说明 |
---|---|
高性能 | 内存读写,毫秒级响应,适合高频验证场景 |
自动过期(TTL) | 可设置验证码有效期(如5分钟),到期自动删除,无需手动清理 |
原子性操作 | 支持 SETEX (设置值+过期时间)、INCR (防刷)等原子命令 |
分布式支持 | 多台服务器可共享同一份验证码数据,避免负载均衡导致的验证失败 |
持久化可选 | 即使重启服务,验证码仍可保留(根据配置) |
代码示例
// 存储验证码(5分钟过期)
await redis.setex(`email:code:${email}`, 300, code); // key: email:code:user@example.com// 验证时比对
const storedCode = await redis.get(`email:code:${email}`);
if (storedCode === userInputCode) {// 验证通过
}
Key 设计建议
- 格式:
业务前缀:唯一标识
(如email:code:user@example.com
) - 防冲突:避免简单键名(如
code
),确保不同业务/用户隔离
2. 其他方案对比
存储方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
内存(Map) | 零延迟,简单 | 重启丢失,无法多机共享 | 开发环境快速原型 |
数据库 | 持久化,结构化查询 | 性能低,需手动清理过期数据 | 不推荐(除非已有数据库) |
Memcached | 高性能 | 无原生过期机制,功能较 Redis 少 | 旧系统兼容 |
3. 生产环境最佳实践
(1) 安全性增强
- 加密存储:对验证码哈希后再存 Redis(避免明文泄露):
const hashedCode = crypto.createHash('sha256').update(code).digest('hex'); await redis.setex(`email:code:${email}`, 300, hashedCode);
- 防暴力破解:限制验证尝试次数(如每分钟3次):
const attempts = await redis.incr(`email:attempts:${email}`); if (attempts > 3) throw new Error('尝试次数过多');
(2) 防刷策略
- 频率限制:同一邮箱/IP 间隔时间发送:
const lastSent = await redis.get(`email:last_sent:${email}`); if (lastSent && Date.now() - lastSent < 60000) {throw new Error('请1分钟后再试'); } await redis.setex(`email:last_sent:${email}`, 60, Date.now());
(3) 高可用配置
- Redis 集群:使用哨兵(Sentinel)或集群模式避免单点故障
- 连接池:通过
ioredis
复用连接,提升性能
4. 完整流程示例
// 发送验证码
app.post('/send-code', async (req, res) => {const { email } = req.body;const code = generateCode(6);// 1. 防刷检查const lastSent = await redis.get(`email:last_sent:${email}`);if (lastSent) throw new Error('操作过于频繁');// 2. 存储验证码(5分钟过期)await redis.setex(`email:code:${email}`, 300, code);// 3. 发送邮件await sendEmail(email, code);// 4. 记录发送时间(60秒内禁止重复发送)await redis.setex(`email:last_sent:${email}`, 60, '1');res.json({ success: true });
});// 验证验证码
app.post('/verify-code', async (req, res) => {const { email, code } = req.body;const storedCode = await redis.get(`email:code:${email}`);if (!storedCode || storedCode !== code) {throw new Error('验证码无效');}// 验证通过后删除 Keyawait redis.del(`email:code:${email}`);res.json({ success: true });
});
5. 决策树
总结
- 99% 场景选 Redis:性能、过期管理、分布式支持完胜其他方案
- 内存(Map)仅用于测试:快速验证逻辑,但无生产价值
- 数据库不推荐:除非业务强依赖 SQL 事务
相关文章:
邮件验证码存储推荐方式
邮件验证码的存储方案需要兼顾 安全性、性能 和 可维护性,以下是详细分析和推荐方案: 1. 推荐方案:Redis(首选) 为什么选择 Redis? 优势说明高性能内存读写,毫秒级响应,适合高频验…...
Allegro 输出生产数据详解
说明 用于PCB裸板的生产可以分别单独创建文件 光绘数据(Gerber)、钻孔(NC Drill)、IPC网表;或者通过ODB++或IPC2581文件(这是一个新格式),它包含生产裸板所需要的所有信息 光绘数据 Artwork Gerber 光绘数据一般包含设计中各个层面的蚀刻线路、阻焊、铅锡、字符等信…...

FastAPI MCP 快速入门教程
目录 什么是 FastAPI MCP?项目设置1. 初始化项目2. 安装依赖3. 项目结构 编写代码创建主应用文件 运行和测试1. 启动服务器2. 使用 MCP Inspector 测试 什么是 FastAPI MCP? FastAPI MCP 是一个将 FastAPI 应用程序转换为 Model Context Protocol (MCP)…...

uni-app学习笔记二十一--pages.json中tabBar设置底部菜单项和图标
如果应用是一个多 tab 应用,可以通过 tabBar 配置项指定一级导航栏,以及 tab 切换时显示的对应页。 在 pages.json 中提供 tabBar 配置,不仅仅是为了方便快速开发导航,更重要的是在App和小程序端提升性能。在这两个平台ÿ…...

【Redis】基本命令
Redis命令行客户端 现在我们已经启动了Redis服务,下面将介绍如何使用redis - cli连接、操作Redis服务。客户端和服务端的交互过程如图1 - 3所示。 redis - cli可以使用两种方式连接Redis服务器。 第一种是交互式方式:通过redis - cli -h {host} -p {p…...
爬虫工具链的详细分类解析
以下是针对爬虫工具链的详细分类解析,涵盖静态页面、动态渲染和框架开发三大场景的技术选型与核心特性: 🧩 一、静态页面抓取(HTML结构固定) 工具组合:Requests BeautifulSoup 适用场景:目标数…...
鸿蒙编译ffmpeg库
下载 ffmpeg 项目 基于如下项目编译的 ffmpeg git clone https://gitcode.com/openharmony-sig/tpc_c_cplusplus.git 配置编译环境 下载 command line tools https://developer.huawei.com/consumer/cn/download/ 导出 OHOS_SDK 环境变量 export OHOS_SDK~/command-line-…...

哈希:闭散列的开放定址法
我还是曾经的那个少年 1.概念 通过其要存储的值与存储的位置建立映射关系。 如:基数排序也是运用了哈希开放定址法的的思想。 弊端:仅适用于数据集中的情况 2.开放定址法 问题:按照上述哈希的方式,向集合插入数据为44ÿ…...

Unity-QFramework框架学习-MVC、Command、Event、Utility、System、BindableProperty
QFramework QFramework简介 QFramework是一套渐进式、快速开发框架,适用于任何类型的游戏及应用项目,它包含一套开发架构和大量的工具集 QFramework的特性 简洁性:QFramework 强调代码的简洁性和易用性,让开发者能够快速上手&a…...

FPGA实现CNN卷积层:高效窗口生成模块设计与验证
我最近在从事一项很有意思的项目,我想在PFGA上部署CNN并实现手写图片的识别。而本篇文章,是我迈出的第一步。具体代码已发布在github上 模块介绍 卷积神经网络(CNN)可以分为卷积层、池化层、激活层、全链接层结构,本篇要实现的&…...

LeetCode 3068.最大节点价值之和:脑筋急转弯+动态规划(O(1)空间)
【LetMeFly】3068.最大节点价值之和:脑筋急转弯动态规划(O(1)空间) 力扣题目链接:https://leetcode.cn/problems/find-the-maximum-sum-of-node-values/ 给你一棵 n 个节点的 无向 树,节点从 0 到 n - 1 编号。树以长…...
2.2HarmonyOS NEXT高性能开发技术:编译优化、内存管理与并发编程实践
HarmonyOS NEXT高性能开发技术:编译优化、内存管理与并发编程实践 在HarmonyOS NEXT全场景设备开发中,高性能是跨端应用体验的核心保障。本章节聚焦ArkCompiler编译优化、内存管理工具及多线程并发编程三大技术模块,结合实战案例解析底层实现…...

BLIP-2
目录 摘要 Abstract BLIP-2 模型框架 预训练策略 模型优势 应用场景 实验 代码 总结 摘要 BLIP-2 是一种基于冻结的图像编码器和大型语言模型的高效视觉语言预训练模型,由 Salesforce 研究团队提出。它在 BLIP 的基础上进一步优化,通过轻量级…...
【Go-6】数据结构与集合
6. 数据结构与集合 数据结构是编程中用于组织和存储数据的方式,直接影响程序的效率和性能。Go语言提供了多种内置的数据结构,如数组、切片、Map和结构体,支持不同类型的数据管理和操作。本章将详细介绍Go语言中的主要数据结构与集合…...

支持向量机(SVM)例题
对于图中所示的线性可分的20个样本数据,利用支持向量机进行预测分类,有三个支持向量 A ( 0 , 2 ) A\left(0, 2\right) A(0,2)、 B ( 2 , 0 ) B\left(2, 0\right) B(2,0) 和 C ( − 1 , − 1 ) C\left(-1, -1\right) C(−1,−1)。 求支持向量机分类器的线…...

SQL中各个子句的执行顺序
select、from、 join、where、order by、group by、having、limit 解释 1) FROM (确定数据源) 查询的执行首先从FROM子句开始,确定数据的来源(表、视图、连接等)。 2) JOIN (如果有JOIN操作) 在FROM子句之后,SQL引擎会执行连接操作(JOIN),…...
PHP下实现RSA的加密,解密,加签和验签
前言: RSA下加密,解密,加签和验签是四种不同的操作,有时候会搞错,记录一下。 1.公钥加密,私钥解密 发送方通过公钥将原数据加密成一个sign参数,相当于就是信息的载体,接收方能通过si…...

本地部署消息代理软件 RabbitMQ 并实现外部访问( Windows 版本 )
RabbitMQ 是由 Erlang 语言开发的 消息中间件,是一种应用程序之间的通信方法。支持多种编程和语言和协议发展,用于实现分布式系统的可靠消息传递和异步通信等方面。 本文将详细介绍如何在 Windows 系统本地部署 RabbitMQ 并结合路由侠实现外网访问本…...
每日c/c++题 备战蓝桥杯(P2240 【深基12.例1】部分背包问题)
P2240 【深基12.例1】部分背包问题 - 详解与代码实现 一、题目概述 阿里巴巴要在承重为 T 的背包中装走尽可能多价值的金币,共有 N 堆金币,每堆金币有总重量和总价值。金币可分割,且分割后单位价格不变。目标是求出能装走的最大价值。 二、…...
Java异步编程:CompletionStage接口详解
CompletionStage 接口分析 接口能力概述 CompletionStage 是 Java 8 引入的接口,用于表示异步计算的一个阶段,它提供了强大的异步编程能力: 链式异步操作:允许将一个异步操作的结果传递给下一个操作组合操作&a…...
Java后端接受前端数据的几种方法
在前后端分离的开发模式中,前端(Vue)与后端(Java)的数据交互有多种格式,下面详细介绍几种常见的格式以及后端对应的接收方式。 一、JSON 格式 前端传输 在 Vue 里,可借助 axios 把数据以 JSO…...
Oracle OCP认证的技术定位怎么样?
一、引言:Oracle OCP认证的技术定位 Oracle Certified Professional(OCP)认证是数据库领域含金量最高的国际认证之一,其核心价值在于培养具备企业级数据库全生命周期管理能力的专业人才。随着数字化转型加速,OCP认证…...
powershell7.5@.net环境@pwsh7.5在部分windows10系统下的运行问题
文章目录 powershell7.5及更高版本和.net 9解决方案 powershell7.5及更高版本和.net 9 相对较新的.Net 9版本在老一些的windows10系统上(比如内核版本号:10.0.19044.1288以及之前的),由于默认启用了CET,导致编译运行失败,需要自己在项目中添加关闭CET的配置语句才能够顺利编译…...

基于微信小程序的垃圾分类系统
博主介绍:java高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言࿰…...
CSS3 渐变、阴影和遮罩的使用
全文目录: 开篇语**前言****1. CSS3 渐变 (Gradient)****1.1 线性渐变 (linear-gradient)****1.2 径向渐变 (radial-gradient)** **2. CSS3 阴影 (Shadow)****2.1 盒子阴影 (box-shadow)****2.2 文本阴影 (text-shadow)** **3. CSS3 遮罩 (Mask)****3.1 基本遮罩 (m…...
Spring Boot 全局配置文件优先级
好的,Spring Boot的全局配置文件优先级是一个非常重要的概念,它决定了在不同位置的同名配置属性以哪个为准。 Spring Boot 全局配置文件优先级核心知识点 📌 文件格式优先级: 在同一目录下,如果同时存在 application.properties 和…...

流媒体基础解析:视频清晰度的关键因素
在视频处理的过程中,编码解码及码率是影响视频清晰度的关键因素。今天,我们将深入探讨这些概念,并解析它们如何共同作用于视频质量。 编码解码概述 编码,简单来说,就是压缩。视频编码的目的是将原始视频数据压缩成较…...

grid网格布局
使用flex布局的痛点 如果使用justify-content: space-between;让子元素两端对齐,自动分配中间间距,假设一行4个,如果每一行都是4的倍数那没任何问题,但如果最后一行是2、3个的时候就会出现下面的状况: /* flex布局 两…...
C#数字金额转中文大写金额:代码解析
C#数字金额转中文大写金额:代码解析 在金融相关的业务场景中,我们常常需要将数字金额转换为中文大写金额,以避免金额被篡改,增加金额的准确性和安全性。本文将深入解析一段 C# 代码,这段代码通过巧妙的设计࿰…...

Vehicle HAL(2)--Vehicle HAL 的启动
目录 1. VehicleService-main 函数分析 2. 构建EmulatedVehicleHal 2.1 EmulatedVehicleHal::EmulatedVehicleHal(xxx) 2.2 EmulatedVehicleHal::initStaticConfig() 2.3 EmulatedVehicleHal::onPropertyValue() 3. 构建VehicleEmulator 4. 构建VehicleHalManager (1)初…...