Java基于redis分布式锁完美解决短信重复发送

        在实际项目开发中,几乎90%都需要实现手机验证码登录场景,10分钟只能发送3次验证码,且验证码发送成功后只能验证一次有效。看似很常见很简单的功能,但是需要做到稳定、用户体验好,还是需要两把刷子的。下面将介绍通过使用redis分布式锁来实现验证码只发送一次的功能:

1.生成验证码

一般生成6位数字组合的验证码,验证码的唯一性要求不高,确保大概率不重复即可。

//生成6位随机数
public static String generate6Random() {
    Integer ran = (int)((Math.random()*9+1)*100000);
    return ran.toString();
}

2.编写发送规则

根据实际项目需求,一般验证码发送规则有要求,下面以“10分钟内发送3条短信”为例:

String smsLimitFlag="-1";
            try {
                Object smsLimitFlagObj = redisService.get("smsLimitFlag:"
                        + mobile);
                Object smsLimitFlagTimeObj = redisService.get("smsLimitFlagTime:"
                        + mobile);
                if (smsLimitFlagTimeObj != null) {
                    String smsLimitFlagObjStr=(String)smsLimitFlagObj;
                    if(smsLimitFlagObjStr!=null&&smsLimitFlagObjStr.length()>0&&Integer.valueOf(smsLimitFlagObjStr).intValue()>=3){
                        smsLimitFlag = "1";
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if("1".equals(smsLimitFlag)){
                return ResultInfoUtils.businessError(ResultStatusCode.DELETE_USER_MISS_ERROR.getReturnCode(),
                        "验证码10分钟内只能获取3条,请稍后再试", mobile);
            }

3.建立redis存储机制

短信发送成功后将发送的时间和手机号存储到redis中。

try {
                    Object smsLimitFlagObj = redisService.get("smsLimitFlag:"
                            + mobile);
                    Object smsLimitFlagTimeObj = redisService.get("smsLimitFlagTime:"
                            + mobile);
                    if (smsLimitFlagTimeObj == null) {
                        redisService.set("smsLimitFlagTime:" + mobile, mobile, 10*60L);
                        redisService.set("smsLimitFlag:" + mobile, "1", 10*60L);
                    }else{
                        if (smsLimitFlagObj != null) {
                            String smsLimitFlagObjStr=(String)smsLimitFlagObj;
                            redisService.set("smsLimitFlag:" + mobile, ""+(Integer.valueOf(smsLimitFlagObjStr).intValue()+1), 10*60L);
                        }else{
                            redisService.set("smsLimitFlag:" + mobile, "1", 10*60L);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

4.采用redis分布式锁,防止短信重复发送

  public void sendSms(String mobile,String smsContent) {
       //加分布式锁
       RLock lock = redisson.getLock("sendSms" + mobile);
       try {
             lock.lock();
             //短信发送逻辑
        } finally {
             //解锁
             lock.unlock();
        }
  }