用户三周前申请的重置邮件还能打开,填完新密码直接生效。reset_token 表里只有 created_at,没有 expires_at 字段,代码也没做过期判断。乍看像缓存或者权限的锅,追下去往往不是。
UPDATE reset_token SET used_at = NOW() WHERE user_id = %s AND used_at IS NULL;
追下去是这么回事:链接的有效期只靠邮件客户端的印象,服务端没有时间约束。token 一旦泄露,长期有效等于长期后门。用完也没作废,同一链接可以反复改密码。
「重置链接过期」这类改动最怕改一半,回滚脚本先准备好,再动生产。一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。
先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。别只看报错的最后一行,往上看几行,真正的起因通常在上面。
具体做法是,表里加 expires_at 签发时写入 三十分钟足够;校验时先比时间 过期直接返回失效;改密成功后把该用户所有未使用的 token 作废;旧 token 定期清理 别让表无限增长。
验收就看一点,把 expires_at 改到昨天再点链接 提示链接已失效 改密后同链接第二次打开也失效。
