在React Native EXPO应用中实现OTP验证流程的技术咨询
手机号OTP身份验证功能实现指南
整体实现流程
- 用户在前端输入手机号,前端做格式校验后提交到后端
- 后端生成6位随机OTP,设置5-15分钟的有效时长,将手机号和OTP、过期时间绑定存入缓存
- 后端调用短信服务接口,把OTP下发到用户输入的手机号
- 用户收到短信后在前端输入OTP,提交到后端校验接口
- 后端校验手机号对应OTP是否正确且未过期,校验通过后生成设备身份凭证返回给前端,存入设备本地存储完成身份绑定
核心模块实现要点
前端侧实现
- 手机号输入校验:如果是国内手机号可以用正则
^1[3-9]\d{9}$做前置校验,适配其他地区手机号修改对应正则规则即可,避免无效请求 - 提交按钮防重复点击:点击后设置60s倒计时,防止重复触发短信发送
- OTP输入框优化:可以做6个独立输入框自动跳转,提升输入体验
- 设备标识采集:校验OTP通过后,可以采集设备UA、设备指纹等信息和身份凭证绑定,后续请求携带凭证即可识别身份
前端基础逻辑示例:
// 手机号格式校验函数 function validatePhone(phone) { const reg = /^1[3-9]\d{9}$/ return reg.test(phone) } // 发送OTP按钮倒计时逻辑 let countDown = 60 function startCountDown(btnDom) { btnDom.disabled = true const timer = setInterval(() => { countDown-- btnDom.innerText = `重新发送(${countDown}s)` if(countDown <= 0) { clearInterval(timer) btnDom.disabled = false btnDom.innerText = '发送验证码' countDown = 60 } }, 1000) }
后端侧实现
- OTP生成规则:生成6位纯数字即可,不要使用复杂字符,降低用户输入成本
- 存储规则:优先使用Redis缓存存储,key设置为
sms:otp:${手机号},value存储OTP值和过期时间,设置过期时间和业务有效期一致,自动清理失效数据 - 频率限制:同一个手机号1分钟内最多发送1次,1小时内最多发送5次,防止恶意刷短信
- 校验逻辑:校验通过后立即删除对应的Redis缓存,避免同一个OTP被重复校验
- 身份凭证生成:校验通过后生成包含用户手机号、设备信息、过期时间的JWT或者SessionId,返回给前端,后续接口请求携带该凭证即可完成身份校验
后端基础逻辑示例(Python+Flask):
import random import redis from flask import Flask, request, jsonify import jwt import datetime app = Flask(__name__) redis_client = redis.Redis(host='localhost', port=6379, db=0) JWT_SECRET = '你的自定义密钥' # 发送OTP接口 @app.route('/send_otp', methods=['POST']) def send_otp(): phone = request.json.get('phone') # 先校验发送频率 freq_key = f'sms:freq:{phone}' if redis_client.get(freq_key): return jsonify({'code': 400, 'msg': '请求过于频繁,请稍后再试'}) # 生成6位OTP otp = random.randint(100000, 999999) # 存储OTP,有效期10分钟 otp_key = f'sms:otp:{phone}' redis_client.setex(otp_key, 60*10, str(otp)) # 设置频率限制:1分钟1次 redis_client.setex(freq_key, 60, '1') # 这里调用你对接的短信服务商发送接口 # sms_service.send(phone, f'您的验证码是{otp},10分钟内有效') return jsonify({'code': 200, 'msg': '验证码发送成功'}) # 校验OTP接口 @app.route('/verify_otp', methods=['POST']) def verify_otp(): phone = request.json.get('phone') input_otp = request.json.get('otp') device_info = request.headers.get('User-Agent') otp_key = f'sms:otp:{phone}' stored_otp = redis_client.get(otp_key) if not stored_otp or stored_otp.decode() != input_otp: return jsonify({'code': 400, 'msg': '验证码错误或已过期'}) # 校验通过立即删除OTP redis_client.delete(otp_key) # 生成JWT凭证,有效期7天 token = jwt.encode({ 'phone': phone, 'device': device_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=7) }, JWT_SECRET, algorithm='HS256') return jsonify({'code': 200, 'msg': '验证成功', 'token': token})
注意事项
- 不要把OTP明文返回给前端,所有生成和校验逻辑都放在后端处理
- 生产环境要给短信接口加签名校验,防止恶意调用刷短信
- OTP有效期不要超过15分钟,降低被盗用风险
- 流量较大的场景可以增加图形验证码作为前置校验,进一步防止恶意刷取短信
内容的提问来源于stack exchange,提问作者Suman Budhathoki
相关产品推荐
相关产品推荐

