使用Springboot StringRedisTemplate 在单位时间内对发送手机号验证码次数做限制,半小时最多发送5次验证码

      通常我们在做通过手机号检验,发送短信验证码之类接口的时候,在单位时间内,要对发送的次数做限制,防止恶意刷接口,短信类接口毕竟还是收费的。

     首先我们需要在springboot 项目中引入Redis

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--springboot2.X默认使用lettuce连接池,需要引入commons-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

      然后在配置文件中配置你redis的地址端口 密码 等

 # Redis
spring:
  redis:
    database: 0
    host: localhost
    password: Botao@2019
    port: 6379
    timeout: 300
    lettuce:
      pool:
       max-active: 200 #连接池最大连接数(使用负值表示没有限制)
       max-idle: 20 # 连接池中的最大空闲连接
       min-idle: 5 #连接池中的最小空闲连接
       max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)

windows 环境安装redis 及配置用户名密码,可以看这

整个流程是,首先我们生成4位的短信验证码,然后把要发送的手机号 验证码 设置过期时间 存入redis ,同时将 手机号,发送的次数同时存入redis 注入两次存入的key 不一样 然后我们设置存入次数 key的过期时间 ,当然我们在发送前,需要查询单位时间内发送的次数有没有超过限制 ,没有限制再发

如图所示

@RestController
@RequestMapping("/app")
public class SendSMSController {

    private static final Logger log = LoggerFactory.getLogger(SendSMSController.class);

    private static final String SYMBOLS = "0123456789";

    private static final Random RANDOM = new SecureRandom();

    @Value("${sms_apikey}")
    private String apikey;

    @Value("${sms_send}")
    private String send;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 发送短信验证码
     * @param userId
     * @param phone
     * @return
     */
    @PostMapping("sys/sendSms")
    public AjaxResult SendSms(String userId, String phone){

        try {
            log.info("用户发送短信验证码:"+userId+","+phone);

            if(StringUtils.isBlank(phone)){

                log.info("参数错误");
                return AjaxResult.error("参数错误");
            }

            //设置key 自动 +1
            long count=stringRedisTemplate.boundValueOps(AppConstants.REDIS_SMS_USER_PREFIX+phone ).increment(1);

            if(count==1){
                //设置 30 分钟过期
                stringRedisTemplate.expire(AppConstants.REDIS_SMS_USER_PREFIX+phone, AppConstants.REDIS_SMS_USER_EXPIRE , TimeUnit.SECONDS);

            }
            if(count > 5){

                log.info("验证码发送频繁,超过了限定的次数");
                return AjaxResult.error("验证码发送频繁,超过了限定的次数");
            }
            //生成随机验证码
            String code=getNonce_str();
            //发送短信
           /* String sms= SendSMS.sendSms(apikey,send,phone,code);
            //查看是否发送成功
            JSONObject json=JSONObject.parseObject(sms);
            String success=json.getString("status");

            if (!success.equals("success")){
//发送失败,次数减回来 -1 表示
                stringRedisTemplate.boundValueOps(AppConstants.REDIS_SMS_USER_PREFIX+phone ).increment(-1);
                return AjaxResult.error("短信发送失败");
            }*/
            //发送成功,放入redis
            stringRedisTemplate.opsForValue().set(AppConstants.REDIS_SMS_PREFIX+phone, code ,AppConstants.REDIS_SMS_EXPIRE,TimeUnit.SECONDS);

            log.info("验证码发送成功");
            return AjaxResult.success("操作成功",code);
        }catch (Exception e){

            log.info("短信验证码发送失败"+ ExceptionUtils.getStackTrace(e));
            return AjaxResult.error("短信验证码发送失败");
        }
    }


    @PostMapping("sys/checkSmsCode")
    public AjaxResult checkSmsCode(String phone,String code){

        try {
            log.info("开始验证手机号验证码:"+phone+","+code);
           String  rediscode=stringRedisTemplate.opsForValue().get(AppConstants.REDIS_SMS_PREFIX+phone);

           if (rediscode ==null || !rediscode.equals(code)){

              return AjaxResult.error("验证码错误");
           }

           log.info("验证码验证成功");
            return AjaxResult.success();
        }catch (Exception e){

            log.info("短信验证码验证失败"+ ExceptionUtils.getStackTrace(e));
            return AjaxResult.error("短信验证码验证失败");
        }
    }

    /**
     * 获取长度为 4 的随机数字
     * @return 随机数字
     * @date
     */
    public static String getNonce_str() {

        // 如果需要4位,那 new char[4] 即可,其他位数同理可得
        char[] nonceChars = new char[4];

        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }

        return new String(nonceChars);
    }


}
AppConstants 类是放缓存 key 的前缀和过期时间 ,大家可以自己定义,顺便把类也贴一下
public class AppConstants {

    public static final String REDIS_KEY_PREFIX = "botao:"; // redis缓存前缀

    /** redis session 前缀*/
    public static final String REDIS_SESSION_PREFIX = "botao:session:";
    /** redis缓存 session失效时间 秒*/
    public static final int REDIS_SESSION_EXPIRE =  60*60*24*30;

    /** redis token 前缀*/
    public static final String REDIS_TOKEN_PREFIX = "botao:token:";
    /** redis缓存 token失效时间 秒*/
    public static final int REDIS_TOKEN_EXPIRE = 60*60*24*30;

    /** redis缓存 验证码 失效时间 秒*/
    public static final int REDIS_SMS_EXPIRE = 60*5;

    /** redis缓存 验证码 失效时间 秒*/
    public static final int REDIS_SMS_USER_EXPIRE = 60*30;

    /** redis sms 前缀*/
    public static final String REDIS_SMS_PREFIX = "botao:sms:";

    /** redis sms 前缀*/
    public static final String REDIS_SMS_USER_PREFIX = "botao:count:sms:";

}

上面设置的是半小时内,发送 5 次 ,经过测试,可以对单位时间内发送次数做限制

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值