spring boot中redis操作Hash踩坑

目录

问题

原因

解决方案


问题

如下代码是获取短信验证码功能,会先检查下前面五分钟内发没发过验证码,也就是有没有手机号对应缓存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分钟
       
 
    }
相关推荐
蓝斯49738 分钟前
[原创]《C#高级GDI+实战:从零开发一个流程图》第章:增加贝塞尔曲线,上、下、左、右连接点
java·c#·流程图
前端工作日常1 小时前
我学习到的Java 的 Service 分层:itf 和 impl 到底是什么?
java·后端
都叫我大帅哥2 小时前
Spring @Transactional 注解完全指南
java·spring
Java成神之路-2 小时前
G1 垃圾回收器 :SATB + TAMS核心机制深度解析
java·jvm
前端工作日常2 小时前
我学习到的JIT即时编译与机器码缓存失效
java·后端
带刺的坐椅2 小时前
Solon 的 10 种 HTTP 服务器:改一行依赖,换一个引擎
java·solon·jetty·undertow·mcp-server·htttp
xcLeigh3 小时前
Go入门:变量声明的五种方式详解
java·开发语言·golang
AI大模型-小华3 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
Mark_ZP4 小时前
【锁1】Synchronized vs ReentrantLock区别
java
立心者04 小时前
SpringBoot中使用TOTP实现MFA(多因素认证)
java·spring boot·后端