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语言中内存的分布、堆区和栈区的区别、标识符的命名规则、定义和…...

Qt Designer工具如何修改MainWindow窗口的标题
Qt Designer工具如何修改MainWindow窗口的标题 在MainWindow的属性编辑器中选择“windowTitle”后面一栏修改成期望的窗口标题名称即可。 按住“ctrlR”即可查看可视化界面的窗口标题...

车辆前向碰撞预警系统性能要求和测试规程
前言 本文整理《GB/T 33577-2017 智能运输系统-车辆前向碰撞预警系统性能要求和测试规程》国标文件关键信息,FCW系统性能和测试右给深层次的认识。 术语和定义 车辆前向碰撞预警系统 forward vehicle collision warning system自车 subject vehicle(SV)目标车辆 target ve…...

C#实现winform中渲染图的展示
在WinForms中实现图形的渲染展示,可以使用GDI绘图技术。下面是一个简单的示例,演示如何在WinForms中展示一个圆形图形,并根据用户输入的半径动态改变圆的大小: 请在Visual Studio中创建一个WinForms应用程序,并将以下…...

JTS库的讲解及使用
JTS(Java Topology Suite)是一套用于创建、操作和分析二维几何对象的Java库。JTS提供了丰富的几何操作和分析功能,是GIS(地理信息系统)应用中的重要工具。以下是JTS库的一些主要功能及其详细使用示例: 1. …...

【C++杂货铺】unordered系列容器
目录 🌈 前言🌈 📁 unordered系列关联式容器 📁 底层结构 📂 哈希概念 📂 哈希冲突 📂 哈希函数 📂 哈希冲突解决 📁 模拟实现 📁 总结 🌈 前…...

模板-初阶
引言: 在C,我们已经学过了函数重载,这使得同名函数具有多个功能。但是还有一种更省力的方法:采用模板。 本文主要介绍以下内容 1. 泛型编程 2. 函数模板 3. 类模板 1.泛型编程 在将这一部分之前,通过一个故事引…...

重载运算符C++---学习笔记
一、笔记 1. 重载运算符基础知识 重载运算符进行的运算和普通数的加减运算不同之处在于重载运算符的操作数为一个一个自定义的对象,所以相应的要对普通的运算符如-*%/的调用方法进行重写,重载的本质还是函数调用 2. 重载运算符的语法 重载运算符的语…...

SpringMVC枚举类型字段处理
在日常的项目开发中经常会遇到一些取值范围固定的字段,例如性别、证件类型、会员等级等,此时我们可以利用枚举来最大程度减少字段的乱定义,统一管理枚举的值。 SpringMVC中对于枚举也有默认的处理策略: 对于RequestParam…...

集成算法:Bagging模型、AdaBoost模型和Stacking模型
概述 目的:让机器学习效果更好,单个不行,集成多个 集成算法 Bagging:训练多个分类器取平均 f ( x ) 1 / M ∑ m 1 M f m ( x ) f(x)1/M\sum^M_{m1}{f_m(x)} f(x)1/M∑m1Mfm(x) Boosting:从弱学习器开始加强&am…...

DW怎么Python:探索Dreamweaver与Python的交织世界
DW怎么Python:探索Dreamweaver与Python的交织世界 在数字世界的广袤天地中,Dreamweaver(简称DW)与Python这两大工具各自闪耀着独特的光芒。DW以其强大的网页设计和开发能力著称,而Python则以其简洁、易读和强大的编程…...