springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制
springboot结合redis发送短信验证码,实现限制发送操作
- 前言(可忽略)
- 实现思路
- 正题
- 效果图示例
- 手机号不符合规则校验图
- 成功发送验证码示例图
- redis中缓存随机数字验证码,2分钟后失效删除redis缓存图
- 验证码有效期内 返回禁止重复发送图
- 验证码24小时内发送达到3次,限制再次发送验证码图
- idea代码控制台输出日志信息图
- 接口压测1万次全部success图
- 使用到的maven依赖
- redis 缓存Key 统一静态管理类
- 返回错误信息枚举定义
- redis缓存util工具类封装
- service验证码发送接口定义和实现代码
- 接口定义
- 接口实现类
- controller业务接口请求代码
- 接口请求测试
- 请求
- 响应
- 结尾
前言(可忽略)
好久没有更新过csdn博客了,从2023年到发布这篇文章之前,感觉有1年没有写代码的状态,大多还是和 工作/日常 有关 影响,有点退步了。 现在找回了写代码的那种状态,找回状态后发现还是要多学习,要始终保持着热爱 虚心钻研之心.
工作之余想到了一些功能点,空闲时间自己实现了下,做个记录。 有时间还是得要多写点业务代码,不然一直在退步.
----好了 不多说了,进入文章正题---</b>
实现思路
调用发送短信接口,输入手机号发送短信。
发送请求后,判断手机号码格式是否正确,格式不正确,返回格式错误信息。
格式正确,进行发送短信,发送成功时,redis中缓存该手机号验证码2分钟 同时缓存24小时发送次数默认1。
如验证码2分钟未失效,再次使用相同手机号码发短信时,返回验证码在有效期内请勿重复发送。
2分钟验证码失效后,会自动删除该验证码缓存。可再次对该手机号发送验证码,再次发送成功后,该号码24小时发送次数缓存值+1,此时短信发送成功第二次。
24小时内重复发送短信测试,直到达到3次后。 发送验证码返回24小时发送验证码3次,发送失败,请于1天后重试。
正题
使用springboot框架结合redis发送验证码, 实现限制功能,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制.
代码中定义短信云平台模板,进行模拟发送真实验证码,可接入阿里云等各种第三方云短信模板,结合各种场景进行验证码后续的业务操作。
// 模拟阿里云发送短信业务, 模板示例值. 可自行对接
String sendMsg = "【提先森的小站】测试阿里云服务短信发送,此次操作验证码为 {} ,该验证码有效期2分钟,请尽快输入验证.";
使用良好优雅的代码规范实现限制发送验证码功能, 加入统一枚举返回错误类型,统一redis缓存key值管理~
以下开始正文 附上效果图示例和完整代码,希望对看到有需要的朋友有所帮助。
如有帮到您,还希望点赞支持一下yo~
效果图示例
手机号不符合规则校验图
成功发送验证码示例图
redis中缓存随机数字验证码,2分钟后失效删除redis缓存图
验证码有效期内 返回禁止重复发送图
验证码24小时内发送达到3次,限制再次发送验证码图
idea代码控制台输出日志信息图
接口压测1万次全部success图
使用到的maven依赖
<!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--hutool 工具使用参考文档:https://hutool.cn/ --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.20</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency>
redis 缓存Key 统一静态管理类
/*** @author zxt* @version 1.0.0* @date 2023年06月20日 11:29:16* @describe redis 缓存Key 统一管理*/
public class RedisKeyConstant {// 发送验证码keypublic static final String USER_REGISTER_KEY = "user:register:send_code:{}";// 手机号24小时内发送验证码次数keypublic static final String USER_REGISTER_COUNT_KEY = "user:register:day_send_count:{}";public static final String USER_INFORMATION_KEY = "user:information:{}";public static final Integer USER_INFORMATION_EXPIRED = 3;public static final String USER_LOGIN_INFORMATION_KEY = "login:info:{}";public static final Integer USER_LOGIN_INFORMATION_EXPIRED = 2;public static final String APPLET_TELECOM_TOKEN_KEY = "user:token:{}";public static final Integer APPLET_TELECOM_TOKEN_EXPIRED = 1;}
返回错误信息枚举定义
/*** @author zxt* @apiNote* @date 2024/4/16 10:23*/
public enum ServiceErrorEnum {SUCCESS(0, "OK!"),SERVER_ERROR(500, "Internal Server Error"),PARAM_FAIL(-1, "Param Fail"),REGISTER_RECAPTCHA_INPUT_ERROR(10000, "注册失败, 验证码输入错误..."),REGISTER_RECAPTCHA_EXPIRE(10001, "注册失败, 手机号验证码信息不存在..."),REGISTER_RECAPTCHA_ISVALID(10002, "验证码在有效期内, 2分钟内请勿重复发送... "),REGISTER_CODE_COUNT_ERROR(10003, "24小时内已发送验证码3次,发送已限制 请于1天后重试..."),REGISTER_PHONENUMBER_ERROR(10004, "请检查手机号码是否符合规范..."),REGISTER_PHONENUMBER_ISREGISTER(10006, "短信发送失败,该手机号已注册..."),REGISTER_PHONENUMBER_EXIST(10007, "该手机号已注册,注册失败..."),LOGIN_PASSWORD_ERROR(10008, "用户名或密码输入错误,登录失败..."),LOGIN_NOREGISTER_ERROR(10009, "手机号未注册,登录失败..."),;/*** code*/private int code;/*** message*/private String message;public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}ServiceErrorEnum(int code, String message) {this.code = code;this.message = message;}
}
redis缓存util工具类封装
/*** spring redis 工具类** @author zxt**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
@Slf4j
public class RedisCache
{@Resourcepublic RedisTemplate redisTemplate;private static final Long SUCCESS = 1L;private static final Integer DEFAULT_EXPIRE_TIME = 30 * 60;/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值*/public <T> void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值* @param timeout 时间* @param timeUnit 时间颗粒度*/public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key){ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 判断 key是否存在** @param key 键* @return true 存在 false不存在*/public Boolean hasKey(String key){return redisTemplate.hasKey(key);}/*** 删除单个对象** @param key*/public boolean deleteObject(final String key){return redisTemplate.delete(key);}}
service验证码发送接口定义和实现代码
接口定义
package com.tiz.third.sevice;import com.baomidou.mybatisplus.extension.service.IService;
import com.tiz.third.pojo.User;import java.util.Map;/*** @author zxt* @date 2024-05-24 17:57* @describe 注册登录接口业务*/
public interface ModelService{/*** 发送验证码* @param phoneNumber* @return*/public String sendVerificationCode(String phoneNumber);/*** 注册* @param user* @return*///.....更多接口实现已略
}
接口实现类
/*** @author zxt* @date 2024-05-24 18:01* @describe*/
@Slf4j
@Service
public class ModelServiceImpl implements ModelService {@Resourceprivate RedisCache redisCache;@Overridepublic String sendVerificationCode(String phoneNumber) {// 模拟阿里云发送短信业务, 模板示例值。 可自行对接String sendMsg = "【提先森的小站】测试阿里云服务短信发送,此次操作验证码为 {} ,该验证码有效期2分钟,请尽快输入验证.";synchronized (this) {boolean phoneIsValid = Validator.isMobile(phoneNumber);if (!phoneIsValid) {log.error("请确认发送验证码手机号格式是否正确");return ServiceErrorEnum.REGISTER_PHONENUMBER_ERROR.getMessage();}//手机验证码keyString msgKey = StringUtils.format(RedisKeyConstant.USER_REGISTER_KEY, phoneNumber);//手机验证码valueString msgValue = StringUtils.format(sendMsg, RandomUtil.randomNumbers(4));//判断手机验证码key是否存在Boolean registerExists = redisCache.hasKey(msgKey);if (registerExists) {log.error("验证码在有效期内,2分钟内请勿重复发送!");return ServiceErrorEnum.REGISTER_RECAPTCHA_ISVALID.getMessage();}//手机验证码1天内获取次数String countKey = StringUtils.format(RedisKeyConstant.USER_REGISTER_COUNT_KEY, phoneNumber);//判断验证码1天发送次数key是否存在Boolean cacheDayCount = redisCache.hasKey(countKey);if (cacheDayCount) {Integer count = redisCache.getCacheObject(countKey);//发送次数值控制Integer newCacheCount = count + 1;if (newCacheCount > 3) {log.error("手机号:{} 注册验证码24小时内已发送3次,发送验证码失败,请于24小时后重试!", phoneNumber);return ServiceErrorEnum.REGISTER_CODE_COUNT_ERROR.getMessage();}log.error("手机号:{} 注册验证码发送成功, 24小时内已发送{}次", phoneNumber, newCacheCount);//缓存手机验证码1天发送次数值+1redisCache.setCacheObject(countKey, newCacheCount);} else {//缓存手机验证码1天发送次数值 默认1redisCache.setCacheObject(countKey, 1, 1, TimeUnit.DAYS);log.error("手机号:{} 注册验证码发送成功, 24小时内已发送{}次", phoneNumber, 1);}//缓存手机验证码2分钟 key+valueredisCache.setCacheObject(msgKey, msgValue, 2, TimeUnit.MINUTES);return "send success";}}
}
controller业务接口请求代码
/*** @author zxt* @date 2024-05-24 18:03* @describe*/
@RequestMapping("/model")
@RestController
@Slf4j
public class ModelController {@Resourceprivate ModelService modelService;/*** 发送手机验证码* @param phoneNumber* @return*/@GetMapping("/send/{phoneNumber}" )public AjaxResult sendVerificationCode(@NotBlank(message = "手机号码不能为空") @PathVariable String phoneNumber) {// 可自己代码定义返回值类型,业务逻辑返回string 此处自己处理返回相对应返回类型return AjaxResult.success(modelService.sendVerificationCode(phoneNumber));}
}
接口请求测试
请求
get请求
localhost:xx/model/send/{手机号}
响应
{"msg": "24小时内已发送验证码3次,发送已限制 请于1天后重试...","code": 200,"data": null,"currentTimeStamp": 1716779182241
}
结尾
该功能 可用于 > 注册、登录 等各种需要发送手机验证码场景,可防盗刷验证码限流,去redis中拿取手机号对应缓存,判断验证码是否正确,去执行一系列的业务操作。
如对您有帮助,点个赞支持一下,感谢支持~
相关文章:

springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制
springboot结合redis发送短信验证码,实现限制发送操作 前言(可忽略)实现思路正题效果图示例手机号不符合规则校验图成功发送验证码示例图redis中缓存随机数字验证码,2分钟后失效删除redis缓存图验证码有效期内 返回禁止重复发送图验证码24小时内发送达到3次…...
【Python】使用 Pandas 统计每行数据中的空值
缘分让我们相遇乱世以外 命运却要我们危难中相爱 也许未来遥远在光年之外 我愿守候未知里为你等待 我没想到为了你我能疯狂到 山崩海啸没有你根本不想逃 我的大脑为了你已经疯狂到 脉搏心跳没有你根本不重要 🎵 邓紫棋《光年之外》 在数据分析…...
1pannel部署onenav导航容器编排模板
onenav导航 1pannel部署onenav导航容器编排模板 networks:1panel-network:external: true services:onenav:container_name: onenavimage: helloz/onenav:latestlabels:createdBy: Appsnetworks:- 1panel-networkports:- 127.0.0.1:{port}:80environment:- TZAsia/Shanghaivol…...

linux--实时性优化
linux--实时性优化 1 介绍2 实时性需求3 代表性实时系统4 嵌入式系统嵌入式软件系统结构处理器时钟节拍多任务机制任务调度方式任务调度算法时间片调度算法优先级调度算法基于优先级的时间片调度算法 5 cyclictest 测试工具命令说明命令分析参数含义 6 linux 实时性改进某版本上…...

React-基础样式控制
组件基础样式方案 React组件基础的样式控制有两种方式 1、行内样式(不推荐) 属性名是多个单词的需要使用驼峰写法 也可以把样式都提取到一个变量里,再赋值到style里 2、class类名控制 classnames优化类名控制 classnames是一个简单的JS库&…...

制作ChatPDF之前端Vue搭建(二)
前端界面 接上篇: 制作ChatPDF之Elasticsearch8.13.4搭建(一) 为了实现一个基于 Vue.js 的前端应用,用户可以上传 PDF 文件,输入查询,并在输出框中显示查询结果,你需要以下步骤: 初始化 Vue …...

汽车IVI中控开发入门及进阶(二十一):DAB和FM 收音机
前言: 在过去的十年里,数字收音机对车载娱乐产生了重大影响。现在,几乎每辆新车都标配了这项技术,这也是我们60%以上的人收听收音机的方式。甚至有传言称,在不久的将来,将永久关闭调频发射机,使许多车载收音机过时。但一些相对年轻的汽车在工厂里仍然没有安装DAB,而且…...
智能sql LLM
DB-GPT:彻底改变数据库与私有LLM技术的交互 智能SQL生成:后端技术与LLM的完美结合 智能SQL生成:后端技术与LLM的完美结合_llm sql-CSDN博客 GitHub - eosphoros-ai/DB-GPT: AI Native Data App Development framework with AWEL(Agentic Wor…...
大聪明教你学Java | 深入浅出聊 Stream.parallel()
前言 🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。 🍊支持作者: 点赞👍、关注💖、留言Ǵ…...

图解大模型分布式并行各种通信原语
背景 在分布式集群上执行大模型任务时候,往往使用到数据并行,流水线并行,张量并行等技术,这些技术本质上也就是对数据进行各种方案的切分,然后放到不同的节点上运算。不同节点在计算的过程中需要对数据分发或者同步等…...

张大哥笔记:下一个风口是什么?
我们经常会问,下一个风口是什么?我们可以大胆预测一下,2024年的风口是什么呢? 40年前,如果你会开车,那就是响当当的铁饭碗; 30年前,如果你会英语和电脑,那也绝对是个人才…...

AI去衣技术中的几何着色:揭秘数字时尚的魔法
在数字化时代,人工智能(AI)正以前所未有的速度改变我们的生活,从智能家居到自动驾驶汽车,再到个性化医疗。然而,AI的影响远不止于此。它正在重塑我们对艺术、设计和时尚的理解。特别是在数字时尚领域&#…...

Leecode---技巧---只出现一次的数字 / 多数元素
题解: 利用异或运算 a⊕a 0 的性质,可用来消除所有出现了两次的元素,最后剩余的即为所得。 class Solution { public:int singleNumber(vector<int>& nums){// 初始化为0int ans 0;for(int x: nums){// 异或操作ans ^ x;}retur…...
为图片设置经纬度信息
一、java实现 小编看了很多技术博客,但是测试要么下载的jar包中的api和博客对不上,要么就是不对,总之没实现 Java 读取图片信息 java 写入 exif 信息 使用Java读取和修改图片的Exif信息 java获取图片的GPS信息 https://drewnoakes.com/code/e…...
密码和密钥的联系与区别
密码和密钥是两个非常重要的概念,但容易混淆这两者,以下内容介绍了它们的联系和区别: 一、定义 密码(Password),在日常语境中,通常指的是个人为了验证自己的身份而设置的一段秘密的字符序列&am…...
C++编程法则365天一天一条(323)main函数执行之前和之后的动作
在C和C程序中,main 函数之前和之后执行的函数是由编译器、链接器和运行时环境共同决定的。以下是一些通常会在这些阶段执行的关键函数: 在 main 函数之前执行的函数 启动代码(Start-up Code): 这是由编译器提供的一段代码&#…...

阿里云短信服务使用(Java)
文章目录 一、流程1.打开短信服务2.提交材料申请资质3.资质通过后,申请短信签名并设置短信模板4.右上角设置AccessKey5.充值 二、参考官方文档调用API1.引入maven依赖2.调用API补充 一、流程 1.打开短信服务 登陆注册阿里云 搜索“短信服务”,点击“免…...

C++17之std::void_t
目录 1.std::void_t 的原理 2.std::void_t 的应用 2.1.判断成员存在性 2.1.1.判断嵌套类型定义 2.1.2 判断成员是否存在 2.2 判断表达式是否合法 2.2.1 判断是否支持前置运算符 2.2.3 判断两个类型是否可做加法运算 3.std::void_t 与 std::enable_if 1.std::void_t 的…...
零基础入门篇①⑥ Python可变序列类型--字典
Python从入门到精通系列专栏面向零基础以及需要进阶的读者倾心打造,9.9元订阅即可享受付费专栏权益,一个专栏带你吃透Python,专栏分为零基础入门篇、模块篇、网络爬虫篇、Web开发篇、办公自动化篇、数据分析篇…学习不断,持续更新,火热订阅中🔥专栏限时一个月(5.8~6.8)重…...
C语言面试题1-10
C语言中的内存管理及相关问题探讨 在C语言编程中,内存管理是一个至关重要的概念,掌握内存的分布及其操作不仅能够提高代码效率,还能避免常见的内存泄漏等问题。本文将详细介绍C语言中内存的分布、堆区和栈区的区别、标识符的命名规则、定义和…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...