1. 项目背景与需求分析
在移动应用开发中,验证码发送功能是用户注册、密码重置等关键环节的标配。但如果不加限制地允许用户频繁点击发送按钮,可能会引发一系列问题:
- 邮件服务滥用:短时间内大量发送验证邮件容易被邮件服务商判定为垃圾邮件,导致发件域名/IP被拉黑
- 服务器压力:每个验证码请求都涉及数据库写入、短信/邮件发送等操作,高频请求会消耗服务器资源
- 用户体验:用户误触或恶意点击会导致接收端被验证码轰炸
我在开发一个Android应用时,就遇到了这样的场景:用户密码重置功能需要邮件验证码,但测试阶段发现有人连续点击发送按钮,导致邮件服务商发出了警告。于是决定实现"30秒内只能发送一次验证码"的限制功能。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案设计
2.1 核心实现思路
实现发送间隔限制的核心逻辑其实很简单:记录最后一次成功发送的时间戳,下次请求时检查当前时间与上次发送时间的差值。但要做好这个功能,需要考虑以下几个关键点:
- 状态存储位置:客户端存储还是服务端存储?
- 时间同步问题:如何防止客户端修改系统时间绕过限制?
- 用户体验:如何直观显示剩余等待时间?
- 异常处理:网络请求失败时如何处理计时?
2.2 方案选型与比较
方案一:纯客户端实现(不推荐)
kotlin复制var lastSendTime = 0L
fun sendVerificationCode() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastSendTime < 30000) {
showToast("请等待${(30000 - (currentTime - lastSendTime))/1000}秒后再试")
return
}
// 发送验证码逻辑
lastSendTime = currentTime
}
缺点:
- 用户可以通过修改系统时间或清除应用数据绕过限制
- 无法防止多设备同时请求
方案二:服务端校验(推荐)
kotlin复制// Android端
fun sendVerificationCode() {
if (!isButtonEnabled) return
viewModelScope.launch {
try {
val response = apiService.sendVerificationCode(email)
if (response.isSuccess) {
startCountDown()
}
} catch (e: Exception) {
handleError(e)
}
}
}
服务端伪代码:
python复制last_send_time = redis.get(f"last_send:{user_ip}")
if last_send_time and current_time - last_send_time < 30:
return {"code": 429, "message": "请求过于频繁"}
# 发送验证码逻辑
redis.setex(f"last_send:{user_ip}", 30, current_time)
优点:
- 防篡改:时间校验在服务端完成
- 全局控制:基于IP或账号限制,防止多设备绕过
- 灵活调整:限制策略可随时在服务端修改
