You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决手机号验证码登录的重复短信及刷新重验证问题?

手机号验证码登录的两个问题解决

我实现了一个手机号+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;
    }
}

问题根源与修复方案

问题本质

  1. 刷新后重新验证:前端把验证码存在全局变量verificationCode里,页面刷新后变量丢失;后端会话虽存了验证码,但前端没在页面加载时读取会话状态,导致必须重新提交手机号。
  2. 重复发验证码:如果直接在页面加载时自动触发短信接口,会导致每次刷新都发新验证码。

具体修复步骤

后端调整

  1. 给验证码加过期时间与手机号绑定:避免验证码长期有效,同时防止不同手机号复用验证码
// 发送短信成功后,新增会话存储内容
$_SESSION['verification_code'] = $message;
$_SESSION['code_expire_time'] = time() + 300; // 5分钟过期
$_SESSION['verified_phone'] = $phone;
$_SESSION['contact_id'] = $contactData['ID']; // 提前存储用户ID,验证成功后用
  1. 增加验证状态查询接口:页面加载时先查是否有未过期的验证码,无需重复发送
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;
}
  1. 优化短信发送逻辑,避免重复发送:如果当前手机号已有未过期验证码,直接返回现有数据
// 在生成验证码前加入判断逻辑
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;
}
// 原有生成验证码和发送短信的逻辑放在后面
  1. 新增验证码验证接口:前端把用户输入的验证码传给后端,由后端对比会话中的数据(前端存验证码不安全)
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;
}

前端调整

  1. 页面加载时自动查询验证状态:根据后端返回的状态决定显示手机号表单还是验证码表单
$(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();
        }
    });
});
  1. 修改验证码提交逻辑:把用户输入的验证码和手机号传给后端验证,而非前端直接对比
// 绑定验证表单的提交事件
$(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:39:50