如何解决手机号验证码登录的重复短信及刷新重验证问题?
手机号验证码登录的两个问题解决
我实现了一个手机号+6位验证码的简易身份验证功能,用户输入验证码即可登录,但遇到两个问题:
- 页面刷新后需要重新验证
- 尝试修复上述问题时,会出现重复发送短信验证码的情况
前端用JS开发,后端为PHP,相关代码如下:
前端验证代码
$(document).on('submit', "#reg", function(e) { e.preventDefault(); var form = $(this); var enteredPhone = $('#phone').val(); sendFormToServer(form, function(data) { $('#error-message').text(""); $('#reg').hide(); $('#regButton').hide(); $('#verificationForm').show(); verificationCode = data.verification_code; }); }); function sendFormToServer(form, callback) { $.ajax({ type: "POST", url: actionUrl, data: form.serialize(), dataType: 'json', success: function(data) { callback(data); }, error: function(xhr, status, error) { handleServerError(xhr); } }); } function lk_auth(data) { const obj = data; if (obj.sendt_to) { setCookie('auth', true); setCookie('contactID', obj.sendt_to, options = {}); setCookie('phone', obj.phone, options = {}); console.log('auth = ' + getCookie('auth') + ' contactID = ' + getCookie('contactID') + 'phone = ' + getCookie('phone')); const $menu = $('<div class="header__menu"></div>'); $menu.append('<span style="display:none" id="documents"></span >'); $menu.append('<span style="display:none" id="chat"></span >'); $menu.append('<span style="display:none" id="payments"></span >'); $('#reg').hide(); $('#verificationForm').hide(); $('#body').append($menu); $('#error-message').hide(); } const $content = $('<div class="div0"></div>'); $content.append('<div class="header__menu">Phone: ' + getCookie('phone') + '</div'); $content.find('.header__menu').css('display', 'none'); $('#content').empty(); $('#content').append($content); }
后端短信发送代码
session_start(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_POST['phone'])) { $phone = trim(str_replace(array('-', ' '), '', $_POST['phone'])); $contactData['msg'] = 'Телефон: ' . $phone; $resultContactID = CRest::call( 'crm.duplicate.findbycomm', array( 'entity_type' => "CONTACT", 'type' => "PHONE", 'values' => array($phone) ) ); if (empty($resultContactID['result']['CONTACT'])) { $contactData['msg'] .= ' No personal account is registered'; http_response_code(403); } elseif (count($resultContactID['result']['CONTACT']) > 1) { $contactData['msg'] .= ' attention there is more than 1 contacts ' . count($resultContactID['result']['CONTACT']) . ' contacts!'; http_response_code(500); } else { $contactData['ID'] = $resultContactID['result']['CONTACT'][0]; $message = generateRandomCode(); $api_token = '*****'; $url = "https://sms.ru/sms/send?api_id=$api_token&to=$phone&msg=$message&json=1"; $resultContactID = CRest::call( 'crm.duplicate.findbycomm', array( 'entity_type' => "CONTACT", 'type' => "PHONE", 'values' => array($phone) ) ); $contactData['ID'] = $resultContactID['result']['CONTACT'][0]; $response = file_get_contents($url); $result = json_decode($response); if ($result->status == "OK") { $_SESSION['verification_code'] = $message; $contactData['status'] = 'sent'; $contactData['message'] = 'SMS sent successfully '; $contactData['sendt_to'] = $contactData['ID']; $contactData['verification_code'] = $message; $contactData['phone'] = $phone; } else { $contactData['status'] = 'error'; $contactData['message'] = 'Error in sending SMS'; } } echo json_encode($contactData, JSON_UNESCAPED_UNICODE); exit; } }
问题根源与修复方案
问题本质
- 刷新后重新验证:前端把验证码存在全局变量
verificationCode里,页面刷新后变量丢失;后端会话虽存了验证码,但前端没在页面加载时读取会话状态,导致必须重新提交手机号。 - 重复发验证码:如果直接在页面加载时自动触发短信接口,会导致每次刷新都发新验证码。
具体修复步骤
后端调整
- 给验证码加过期时间与手机号绑定:避免验证码长期有效,同时防止不同手机号复用验证码
// 发送短信成功后,新增会话存储内容 $_SESSION['verification_code'] = $message; $_SESSION['code_expire_time'] = time() + 300; // 5分钟过期 $_SESSION['verified_phone'] = $phone; $_SESSION['contact_id'] = $contactData['ID']; // 提前存储用户ID,验证成功后用
- 增加验证状态查询接口:页面加载时先查是否有未过期的验证码,无需重复发送
session_start(); if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['check_status'])) { $response = []; if (isset($_SESSION['verification_code'], $_SESSION['code_expire_time'], $_SESSION['verified_phone']) && $_SESSION['code_expire_time'] > time()) { $response['status'] = 'pending'; $response['phone'] = $_SESSION['verified_phone']; // 注意:生产环境不要返回真实验证码给前端,这里仅作演示,实际让用户输入后传给后端验证 $response['verification_code'] = $_SESSION['verification_code']; } else { $response['status'] = 'expired'; } echo json_encode($response); exit; }
- 优化短信发送逻辑,避免重复发送:如果当前手机号已有未过期验证码,直接返回现有数据
// 在生成验证码前加入判断逻辑 if (isset($_SESSION['verification_code'], $_SESSION['code_expire_time'], $_SESSION['verified_phone']) && $_SESSION['verified_phone'] == $phone && $_SESSION['code_expire_time'] > time()) { $contactData['status'] = 'sent'; $contactData['message'] = '已有未过期验证码'; $contactData['sendt_to'] = $contactData['ID']; $contactData['verification_code'] = $_SESSION['verification_code']; $contactData['phone'] = $phone; echo json_encode($contactData); exit; } // 原有生成验证码和发送短信的逻辑放在后面
- 新增验证码验证接口:前端把用户输入的验证码传给后端,由后端对比会话中的数据(前端存验证码不安全)
session_start(); if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['verify_code'])) { $phone = trim(str_replace(array('-', ' '), '', $_POST['phone'])); $enteredCode = $_POST['code']; $response = []; if (isset($_SESSION['verification_code'], $_SESSION['code_expire_time'], $_SESSION['verified_phone'], $_SESSION['contact_id']) && $_SESSION['verified_phone'] == $phone && $_SESSION['code_expire_time'] > time() && $_SESSION['verification_code'] == $enteredCode) { // 验证成功,清理会话中的验证码数据 unset($_SESSION['verification_code']); unset($_SESSION['code_expire_time']); unset($_SESSION['verified_phone']); $response['status'] = 'success'; $response['sendt_to'] = $_SESSION['contact_id']; $response['phone'] = $phone; } else { $response['status'] = 'error'; $response['message'] = '验证码错误或已过期'; } echo json_encode($response); exit; }
前端调整
- 页面加载时自动查询验证状态:根据后端返回的状态决定显示手机号表单还是验证码表单
$(document).ready(function() { $.ajax({ type: "GET", url: actionUrl + '?check_status=1', dataType: 'json', success: function(data) { if (data.status === 'pending') { // 存在未过期验证码,直接显示验证表单 $('#reg').hide(); $('#regButton').hide(); $('#verificationForm').show(); verificationCode = data.verification_code; // 生产环境可移除,改为用户输入后传后端验证 } else { // 无有效验证码,显示手机号输入表单 $('#reg').show(); $('#regButton').show(); $('#verificationForm').hide(); } }, error: function() { // 请求失败,默认显示手机号表单 $('#reg').show(); $('#regButton').show(); $('#verificationForm').hide(); } }); });
- 修改验证码提交逻辑:把用户输入的验证码和手机号传给后端验证,而非前端直接对比
// 绑定验证表单的提交事件 $(document).on('submit', "#verificationForm", function(e) { e.preventDefault(); var enteredCode = $('#verification_code').val(); var phone = getCookie('phone') || $('#phone').val(); // 从cookie或输入框获取手机号 $.ajax({ type: "POST", url: actionUrl + '?verify_code=1', data: {phone: phone, code: enteredCode}, dataType: 'json', success: function(data) { if (data.status === 'success') { lk_auth(data); // 验证成功,执行登录后的逻辑 } else { $('#error-message').text(data.message || '验证码错误或已过期'); } }, error: function() { $('#error-message').text('验证失败,请重试'); } }); });
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

