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

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

码农世界 2024-05-29 后端 76 次浏览 0个评论

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

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

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


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

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


            效果图示例

            手机号不符合规则校验图

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

            成功发送验证码示例图

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

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

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

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

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

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

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

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

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

            接口压测1万次全部success图

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


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

            使用到的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次限制

            结尾

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

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

转载请注明来自码农世界,本文标题:《springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,76人围观)参与讨论

还没有评论,来说两句吧...

Top