最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Spring Boot如何利用Redis实现验证码限流与存储?
时间:2026-08-16 10:20:49 编辑:袖梨 来源:一聚教程网
必须用StringRedisTemplate.opsForValue().set()显式设过期时间,限流与验证码键须分离;手机号限流需分60秒时间戳键和24小时计数键;校验验证码应使用getAndDelete保证一次性消费。
直接结论:必须用 StringRedisTemplate + opsForValue().set() 显式传入过期时间,不能用 RedisTemplate.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 会把分钟级和日级计数混在一起。
- 60 秒间隔控制:
sms:last_send:+8613812345678存时间戳,EXPIRE设为 60s,发前先GET判断是否距上次发送不足 60 秒 - 24 小时频次限制:
sms:count:day:+8613812345678用INCR+EXPIRE(设 86400s),每次发完自增,超 3 就拒绝 - 千万别把两个逻辑塞进同一个 key,比如
sms:limit:+86138...—— 你无法原子地同时更新时间和计数
校验验证码时要不要立刻删 key
只靠 TTL 不够。用户连续输错 5 次,每次校验都 GET 一次,Redis 承担了无效查询压力;更严重的是,如果验证码被截获重放,TTL 内还能反复用。
- 推荐用
stringRedisTemplate.opsForValue().getAndDelete("login:code:138xxxx1234") - 它原子性完成“读取 + 删除”,确保一次性消费
- 如果返回 null,说明已失效或不存在,直接拒绝登录,不用再查一遍
- 注意:getAndDelete 返回的是 String,不是 Boolean,判空用
StringUtils.hasText()而非!= null
IP 限流和手机号限流能共用一套逻辑吗
不能硬套。IP 是粗粒度防护(防脚本批量请求),手机号是细粒度业务控制(防用户自己刷),二者触发条件、阈值、惩罚方式都不同。
- IP 限流 key 示例:
sms:ip_limit:192.168.1.100,1 分钟最多 5 次,超限后直接 503 - 手机号限流 key 示例:
sms:phone_limit:+86138...,60 秒 1 次 + 24 小时 3 次,超限返回明确提示“今日已达上限” - 关键区别:IP 限流失败应快速失败(不进业务逻辑),手机号限流失败要给用户友好提示并记录日志
真正麻烦的不是写几行 Redis 命令,而是键命名规则、过期时间粒度、以及 get/delete 的原子性选择——这些细节一旦错位,轻则限流失效,重则误伤正常用户。别省那几行代码,每个键的用途和生命周期,得在注释里写清楚。
相关文章
- 电信华为路由器tc7102怎么设置(电信华为路由器tc7102设置方法) 08-16
- 《梦幻西游》撒豆成兵场景如何进入-撒豆成兵玩法的进入方式 08-16
- 流放之路2 0.5辅助宝石改动一览 08-16
- 华为路由器插件无法安装怎么办(华为路由器插件无法安装如何解决) 08-16
- 梦幻西游五庄观装备搭配69级 08-16
- 流放之路2 0.5天赋树改动一览 08-16