必须用StringRedisTemplate.opsForValue().set()显式设过期时间,限流与验证码键须分离;手机号限流需分60秒时间戳键和24小时计数键;校验验证码应使用getAndDelete保证一次性消费。
直接结论:必须用 StringRedisTemplate + opsForValue().set() 显式传入过期时间,不能用 RedisTemplate.set();限流和存储要分键设计,否则会相互干扰甚至误封用户。
这个方法签名是 set(K key, V value),没有 timeout 参数。你如果写了 redisTemplate.set("key", "123456"),它会存进去但不设 TTL,等同于永久有效(直到手动删或内存淘汰)。更隐蔽的问题是:如果你声明的是 RedisTemplate,value 是字符串却走 JDK 序列化,Redis 里看到的是乱码字节流,get() 返回 null 或报错。
StringRedisTemplate(或显式配置 String 序列化器的 RedisTemplate)stringRedisTemplate.opsForValue().set("login:code:138xxxx1234", "a7f9k", 5, TimeUnit.MINUTES)
TimeUnit,就等于没过期限流目标不是“禁止发短信”,而是“防刷”:同一手机号 60 秒内最多发 1 次,24 小时最多 3 次。这两个维度必须拆开存,否则用一个 key 做 INCR 会把分钟级和日级计数混在一起。
sms:last_send:+8613812345678 存时间戳,EXPIRE 设为 60s,发前先 GET 判断是否距上次发送不足 60 秒sms:count:day:+8613812345678 用 INCR + EXPIRE(设 86400s),每次发完自增,超 3 就拒绝sms:limit:+86138... —— 你无法原子地同时更新时间和计数只靠 TTL 不够。用户连续输错 5 次,每次校验都 GET 一次,Redis 承担了无效查询压力;更严重的是,如果验证码被截获重放,TTL 内还能反复用。
stringRedisTemplate.opsForValue().getAndDelete("login:code:138xxxx1234")
StringUtils.hasText() 而非 != null
不能硬套。IP 是粗粒度防护(防脚本批量请求),手机号是细粒度业务控制(防用户自己刷),二者触发条件、阈值、惩罚方式都不同。
sms:ip_limit:192.168.1.100,1 分钟最多 5 次,超限后直接 503sms:phone_limit:+86138...,60 秒 1 次 + 24 小时 3 次,超限返回明确提示“今日已达上限”真正麻烦的不是写几行 Redis 命令,而是键命名规则、过期时间粒度、以及 get/delete 的原子性选择——这些细节一旦错位,轻则限流失效,重则误伤正常用户。别省那几行代码,每个键的用途和生命周期,得在注释里写清楚。
小米路由器3G怎么恢复出厂设置(小米路由器3G该如何恢复出厂设置)
小米路由器3g和4a千兆版哪个好(小米路由器3g和4a千兆版对比区别)
Sensor Tower:ChatGPT全球份额跌破50%,Gemini与Claude加速追赶
OpenAI提速狂飙16倍!GPT-5.6多智能体V2上线,741轮怪物对话1秒打开
“十五五”时期 煤矿危险繁重岗位将由机器人替代
waytouniverse/ppt-generator:从 Markdown 大纲生成风格统一的 PPT 图片