短信验证码存在 redis 里,TTL 十分钟,校验函数只做取值比对,通过后没有删除。同一个码在十分钟内提交了七次绑定请求,七次全成功。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

ok = r.execute_command('GETDEL', f'sms:{phone}')
if not ok or ok != code:
    return err('验证码无效')

校验和作废是两个动作,代码只做了前者。键的过期时间成了唯一约束,窗口期内可以无限复用。并发下多个请求同时读到同一个值,都能通过。这一点先确认清楚,后面就不用反复试。

「验证码一次性」相关的验证我通常做两遍,改完一次、隔天再看一次,确认不是临时生效。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

具体做法是,校验通过后立刻删除键 用 DEL 的返回值判断是否第一次消费;校验和删除合成一个原子操作 lua 或者 GETDEL;输错也计数 超过五次直接作废;绑定接口再加一层幂等 同一个码只允许绑一个账号。

改完照着核一遍,同一个码第二次提交提示验证码无效 十分钟内只能成功绑定一次。

作者 9823189