springboot结合redis发送短信验证码,实现限制发送操作
- 前言(可忽略)
- 实现思路
- 正题
- 效果图示例
- 手机号不符合规则校验图
- 成功发送验证码示例图
- redis中缓存随机数字验证码,2分钟后失效删除redis缓存图
- 验证码有效期内 返回禁止重复发送图
- 验证码24小时内发送达到3次,限制再次发送验证码图
- idea代码控制台输出日志信息图
- 接口压测1万次全部success图
- 使用到的maven依赖
- redis 缓存Key 统一静态管理类
- 返回错误信息枚举定义
- redis缓存util工具类封装
- service验证码发送接口定义和实现代码
- 接口定义
- 接口实现类
- controller业务接口请求代码
- 接口请求测试
- 请求
- 响应
- 结尾
前言(可忽略)
好久没有更新过csdn博客了,从2023年到发布这篇文章之前,感觉有1年没有写代码的状态,大多还是和 工作/日常 有关 影响,有点退步了。 现在找回了写代码的那种状态,找回状态后发现还是要多学习,要始终保持着热爱 虚心钻研之心.
工作之余想到了一些功能点,空闲时间自己实现了下,做个记录。 有时间还是得要多写点业务代码,不然一直在退步.
----好了 不多说了,进入文章正题---
实现思路
调用发送短信接口,输入手机号发送短信。
发送请求后,判断手机号码格式是否正确,格式不正确,返回格式错误信息。
格式正确,进行发送短信,发送成功时,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依赖
org.springframework.boot spring-boot-starter-data-redis cn.hutool hutool-all 5.8.20 org.projectlombok lombok 1.18.10 redis 缓存Key 统一静态管理类
/** * @author zxt * @version 1.0.0 * @date 2023年06月20日 11:29:16 * @describe redis 缓存Key 统一管理 */ public class RedisKeyConstant { // 发送验证码key public static final String USER_REGISTER_KEY = "user:register:send_code:{}"; // 手机号24小时内发送验证码次数key public 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 { @Resource public RedisTemplate redisTemplate; private static final Long SUCCESS = 1L; private static final Integer DEFAULT_EXPIRE_TIME = 30 * 60; /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */ public
void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 */ public 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 getCacheObject(final String key) { ValueOperations 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 { @Resource private RedisCache redisCache; @Override public String sendVerificationCode(String phoneNumber) { // 模拟阿里云发送短信业务, 模板示例值。 可自行对接 String sendMsg = "【提先森的小站】测试阿里云服务短信发送,此次操作验证码为 {} ,该验证码有效期2分钟,请尽快输入验证."; synchronized (this) { boolean phoneIsValid = Validator.isMobile(phoneNumber); if (!phoneIsValid) { log.error("请确认发送验证码手机号格式是否正确"); return ServiceErrorEnum.REGISTER_PHONENUMBER_ERROR.getMessage(); } //手机验证码key String msgKey = StringUtils.format(RedisKeyConstant.USER_REGISTER_KEY, phoneNumber); //手机验证码value String 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天发送次数值+1 redisCache.setCacheObject(countKey, newCacheCount); } else { //缓存手机验证码1天发送次数值 默认1 redisCache.setCacheObject(countKey, 1, 1, TimeUnit.DAYS); log.error("手机号:{} 注册验证码发送成功, 24小时内已发送{}次", phoneNumber, 1); } //缓存手机验证码2分钟 key+value redisCache.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 { @Resource private 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中拿取手机号对应缓存,判断验证码是否正确,去执行一系列的业务操作。
如对您有帮助,点个赞支持一下,感谢支持~
还没有评论,来说两句吧...