目录
问题
如下代码是获取短信验证码功能,会先检查下前面五分钟内发没发过验证码,也就是有没有手机号对应缓存key,有的话刷新过期时间,没有就缓存一下设置过期时间为5分钟。
但是经过测试在第一次发送时缓存的key没有设置过期时间,也就是说永不过期,当再发送一次后过期时间就正常刷新为5分钟了。
java
@Override
public void getSmsCaptcha(String phone) {
String hashKey = "login:sms:captcha:" + phone;
BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(hashKey);
// 初始检查
String lastSendTimestamp = hashOps.get("lastSendTimestamp");
String sendCount = hashOps.get("sendCount");
String captcha = hashOps.get("captcha");
hashOps.expire(5, TimeUnit.MINUTES); // 设置过期时间为5分钟
// 判断发送次数是否超过限制
if (StringUtils.isNotBlank(sendCount) && Integer.parseInt(sendCount) >= 5) {
hashOps.expire(24, TimeUnit.HOURS); // 重新设置过期时间为24H
throw new GeneralBusinessException("发送次数过多,请24H后再试");
}
// 判断发送频率是否过高
if (StringUtils.isNotBlank(lastSendTimestamp)) {
long lastSendTime = Long.parseLong(lastSendTimestamp);
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - lastSendTime;
long interval = 60 * 1000; // 60秒
if (elapsedTime < interval) {
throw new GeneralBusinessException("发送短信过于频繁,请稍后再试");
}
}
// 更新发送次数
int newSendCount = StringUtils.isNotBlank(sendCount) ? Integer.parseInt(sendCount) + 1 : 1;
// 生成新验证码
if (StringUtils.isBlank(captcha)) {
captcha = RandomStringUtils.randomNumeric(6);
}
// 发送短信
if (!smsUtil.sendSms(phone, captcha)) {
throw new GeneralBusinessException("发送短信失败");
}
// 更新 Redis 中的信息
hashOps.put("captcha", captcha);
hashOps.put("lastSendTimestamp", String.valueOf(System.currentTimeMillis()));
hashOps.put("sendCount", String.valueOf(newSendCount));
}
原因
java
BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(hashKey);
使用 boundHashOps 方法时指定的 hashKey 在 Redis 中不存在,那么 boundHashOps 将返回一个空的 BoundHashOperations 对象。这个对象仍然是可用的,但不包含任何数据。当第一次发送时,上面代码会先获取这个key,并设置过期时间为5分钟,但其实这个key不存在导致设置过期时间失败但是没有任何报错,当第二次发送时,检查到这个key存在了,过期时间就设置成功了。
解决方案
把刷新缓存时间操作放在put数据之后,这样保证了这个key存在后再设置过期时间。
java
@Override
public void getSmsCaptcha(String phone) {
String hashKey = "login:sms:captcha:" + phone;
BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(hashKey);
// 初始检查
String lastSendTimestamp = hashOps.get("lastSendTimestamp");
String sendCount = hashOps.get("sendCount");
String captcha = hashOps.get("captcha");
//。。。。。。。。。。。。。。。。。。。。。。。
// 更新 Redis 中的信息
hashOps.put("captcha", captcha);
hashOps.put("lastSendTimestamp", String.valueOf(System.currentTimeMillis()));
hashOps.put("sendCount", String.valueOf(newSendCount));
//设置数据后再设置过期时间
hashOps.expire(5, TimeUnit.MINUTES); // 设置过期时间为5分钟
}