You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Django生成30分钟有效、手机号唯一的基于时间的OTP

嘿,这个问题我太有共鸣了!之前做短信验证的时候也被这个坑过——用户狂点获取验证码,结果一堆短信过来,根本分不清哪个是有效的。不过基于时间窗口的OTP刚好能解决这个问题,给你分享个在Django里的实现方案:

核心思路

我们要实现的是同一个手机号在固定时间窗口(比如30分钟)内,无论请求多少次,生成的OTP都是完全相同的。核心逻辑是:

  • 以「手机号 + 当前时间窗口标识 + 安全密钥」为输入,通过哈希函数生成固定的6位数字OTP
  • 时间窗口标识由当前时间戳除以窗口秒数(30*60=1800秒)得到,同一个窗口内这个值不会变
实现代码

1. 生成基于时间的OTP

import hashlib
import time
from django.conf import settings

def generate_time_based_otp(phone_number: str, window_minutes: int = 30) -> str:
    # 计算当前所属的时间窗口(每30分钟一个窗口)
    window_seconds = window_minutes * 60
    current_window = int(time.time()) // window_seconds
    
    # 组合加密因子:手机号+时间窗口+安全密钥(密钥从settings读取,避免硬编码)
    data = f"{phone_number}{current_window}{settings.OTP_SECRET_KEY}"
    
    # 使用SHA-256哈希,确保结果唯一性和安全性
    hash_obj = hashlib.sha256(data.encode('utf-8'))
    hash_hex = hash_obj.hexdigest()
    
    # 将十六进制哈希转成十进制,取最后6位作为OTP,不足6位补0
    otp = str(int(hash_hex, 16))[-6:].zfill(6)
    
    return otp

2. 验证OTP的有效性

为了避免用户刚好在窗口切换时收到短信(比如30分钟整的时候),我们可以允许一定的时间漂移,比如验证当前窗口和前一个窗口的OTP:

def verify_time_based_otp(phone_number: str, otp: str, window_minutes: int = 30, allowed_drift_windows: int = 1) -> bool:
    window_seconds = window_minutes * 60
    current_window = int(time.time()) // window_seconds
    
    # 遍历允许的时间窗口范围(默认包含当前窗口和前后各1个窗口)
    for window_offset in range(-allowed_drift_windows, allowed_drift_windows + 1):
        target_window = current_window + window_offset
        data = f"{phone_number}{target_window}{settings.OTP_SECRET_KEY}"
        
        hash_obj = hashlib.sha256(data.encode('utf-8'))
        hash_hex = hash_obj.hexdigest()
        expected_otp = str(int(hash_hex, 16))[-6:].zfill(6)
        
        if expected_otp == otp:
            return True
    return False
关键细节与注意事项
  • 安全密钥配置:在settings.py里添加一个随机的长字符串作为密钥,比如:
    OTP_SECRET_KEY = get_random_string(50)  # 可以用Django的get_random_string生成
    
    这个密钥一定要保密,不能泄露,否则别人可以伪造OTP。
  • 无需存储OTP:因为OTP是实时计算的,不需要把OTP存在数据库里,既节省存储,又避免旧OTP的干扰。
  • 请求频率限制:建议给获取验证码的接口加频率限制(比如同一个手机号1分钟内最多请求3次),防止恶意请求。
  • 用户引导:在短信内容里加上提示,比如「此验证码30分钟内有效,无需重复获取」,减少用户重复请求的行为。

内容的提问来源于stack exchange,提问作者Harsh

火山引擎 最新活动