验证码场景必须用set而非hset,因其是单值、短时效、高并发读写,setex可原子写入并设过期,避免分步操作导致永不过期脏数据;hset字段结构冗余且不支持单field过期。

Redis 存储验证码时为什么要用 SET 而不是 HSET
因为验证码是单值、短时效、高并发读写的场景,SET 配合 EX 参数能原子性地写入并设置过期时间,避免先 SET 再 EXPIRE 可能发生的竞态。用 HSET 多余——你不需要字段结构,也不需要后续对同一个 key 做多字段更新。
实操建议:
提供使用 django-unfold (v0.56+) 构建高级 Django 管理界面的专业指导。当用户提及 django-unfold、unfold admin 等内容时使用此技能。
- 用
redis_client.setex(key, expire_seconds, code)(Python redis 库封装好的原子操作) - key 命名建议带业务前缀和用户标识,比如
verify:phone:13800138000,避免冲突 - 不要把手机号直接拼进 value,value 只存 4–6 位纯数字验证码,防止 Redis 日志或监控意外泄露敏感信息
Django 视图里怎么安全地生成并下发验证码
核心是「生成 → 存 Redis → 返回成功」三步必须原子,且不能暴露真实手机号给前端。常见错误是前端传手机号过来,后端没做频率限制就直接发,结果被刷爆短信接口。
实操建议:
- 用
time.time()+ 手机号算一个滑动窗口 key,比如f"rate:phone:{phone}:{int(time.time() // 60)}",用INCR+EXPIRE控制 1 分钟最多 3 次请求 - 验证码用
random.randint(100000, 999999)或更安全的secrets.randbelow(900000) + 100000 - 下发接口返回
{"sent": true, "countdown": 60},倒计时由前端自己走,后端不参与计时逻辑——只管校验时查 Redis 是否存在且匹配
用户提交验证码时,为什么校验失败但 Redis 里还能查到
典型原因是没在比对成功后立刻删掉 key,导致同一验证码被重复使用;或者用了 GET 但没配 DEL,中间出错(比如数据库写入失败)导致 key 残留。
实操建议:
- 用
redis_client.getdel(key)(Redis 6.2+)或手动GET+DEL组合,确保“读即失效” - 校验逻辑必须包含:检查 key 是否存在 → 取值 → 比对 → 删除 → 再处理业务(如登录、重置密码),顺序不能颠倒
- 如果用 Django 的
cache接口(如cache.get()),注意它不支持原子删除,得切回原生 redis client
倒计时前端显示不准,后端该怎么配合
前端倒计时只是体验优化,真实有效性完全依赖后端 Redis 过期时间。不准的根源常是前后端时间不同步,或前端没监听到发送失败、网络延迟等异常。
实操建议:
- 后端下发时,除了
countdown,再加一个expires_at时间戳(UTC 秒级),前端用它算剩余秒数,比单纯减 60 更可靠 - 前端倒计时到 0 后,按钮变灰但不要禁用请求——因为可能刚巧这时网络延迟导致请求还没发出去,后端仍应接受并校验
- Redis key 过期时间建议设为 5 分钟,而前端倒计时只走 60 秒,留出缓冲窗口,避免“前端显示已过期但 Redis 还没删”的尴尬
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










