springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制

2024-06-04 2507阅读

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次限制.

            代码中定义短信云平台模板,进行模拟发送真实验证码,可接入阿里云等各种第三方云短信模板,结合各种场景进行验证码后续的业务操作

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第1张

            // 模拟阿里云发送短信业务, 模板示例值.  可自行对接
            String sendMsg = "【提先森的小站】测试阿里云服务短信发送,此次操作验证码为 {} ,该验证码有效期2分钟,请尽快输入验证.";
            

            使用良好优雅的代码规范实现限制发送验证码功能, 加入统一枚举返回错误类型,统一redis缓存key值管理~


            以下开始正文 附上效果图示例和完整代码,希望对看到有需要的朋友有所帮助。

            如有帮到您,还希望点赞支持一下yo~


            效果图示例

            手机号不符合规则校验图

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第2张

            成功发送验证码示例图

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第3张

            redis中缓存随机数字验证码,2分钟后失效删除redis缓存图

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第4张

            验证码有效期内 返回禁止重复发送图

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第5张

            验证码24小时内发送达到3次,限制再次发送验证码图

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第6张

            idea代码控制台输出日志信息图

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第7张

            接口压测1万次全部success图

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第8张


            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第9张

            使用到的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
            }
            

            springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制 第10张

            结尾

            该功能 可用于 > 注册、登录 等各种需要发送手机验证码场景,可防盗刷验证码限流,去redis中拿取手机号对应缓存,判断验证码是否正确,去执行一系列的业务操作。

            如对您有帮助,点个赞支持一下,感谢支持~


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]