تایید شماره موبایل با SMS در PHP (احراز هویت پیامکی)

تایید شماره موبایل با SMS در PHP (احراز هویت پیامکی)

تایید شماره موبایل PHP – در این آموزش با استفاده از api پیامکی که آموزش دادیم می خواهیم که سیستم ثبت نام پیامکی را در php بسازیم .

تا زمانی که کد ارسال شده به شماره را در فیلد خواسته شده وارد نکند امکان ثبت نام وجود نخواهد داشت و البته هر کد یکبار مصرف تنها 2 دقیقه اعتبار دارد و بعد از آن منقضی خواهد شد .

همچنین اگر برای هر کد تنها 3 بار امکان وارد سازی کد تایید وجود دارد و بیش از آن کد منقضی خواهد شد و حتی اگر برای بار 4 درست هم وارد شود باز هم کاربر نامعتبر دریافت می کند .

دموی پروژه :


در این سیستم فایل هایی شامل :

  1. sign.up.php : صفحه ثبت نام کاربر با شماره همراه
  2. sign.up.verify.php : صفحه وارد کردن کد تایید ارسال شده
  3. sign.up.complete.php : صفحه ای پس از دریافت کد تایید صحیح از کاربر خواسته می شود تا پروفایل خود را کامل نماید ( نام کاربری ، نام کامل )
  4. view.php : صفحه ای که پس از فعال شدن حساب کاربر هدایت می شود که اطلاعات فردی خود را مشاهده نماید .
  5. فایل توابع functions.php : اکثر تابع های کاربردی را در این اسکریپت داریم که از جمله ( ساخت توکن برای اعتبار سنجی ، handle کردن فرم ها )
  6. فایل توابع misc.php : توابع متفرقه از جمله ( بررسی Regex ، ارسال post ، ارسال پیامک ، دریافت اطلاعات کاربر از طریق کوکی )
  7. فایل توابع db.php : توابع مربوط به دیتابیس ( اتصال ، ساخت ، دریافت ، بروزرسانی )


1- اسکریپت sign.up.php

<?php
require_once "parts/header.php";
?>

<h1 class="text-center">ثبت نام</h1>


<?php if (!@$handler_result || isset($handler_result['status']) && !$handler_result['status']) : ?>
    <form method="POST" class="col-6 m-auto">
        <label class="mb-2" for="phone_number">شماره همراه</label>
        <input type="text" name="phone_number" id="phone_number" class="form-control" dir="ltr" placeholder="09*********">
        <input type="hidden" name="handler" value="handler_post_sign_up">
        <div class="text-center mt-4">
            <input type="submit" class="btn btn-outline-success" value="ارسال">
        </div>
    </form>

<?php elseif (@$handler_result['status'] == 1 || @$handler_result['status'] == -1) : ?>
    <form method="POST" class="col-6 m-auto">
        <p class="bg-<?= $handler_result['status'] == 1 ? "success text-white" : "warning text-dark" ?> p-2 rounded text-center"><?= $handler_result['message'] ?></p>
        <label class="mb-2" for="verify_code">کد تایید</label>
        <input type="number" name="verify_code" id="verify_code" class="form-control" dir="ltr" placeholder="* * * *">
        <input type="hidden" name="handler" value="handler_post_sign_up_verify">
        <input type="hidden" name="id" value="<?= $handler_result['data'] ?>">
        <div class="text-center mt-4">
            <input type="submit" class="btn btn-outline-success" value="ارسال">
        </div>
    </form>

<?php endif; ?>

<?php



?>

<?php
require_once "parts/footer.php";
?>


2- اسکریپت sign.up.verify.php

<?php
require_once "parts/header.php";
?>

<h1 class="text-center">تکمیل پروفایل</h1>

<form method="POST" class="col-6 m-auto">
    <label class="mb-2" for="phone_number">شماره همراه</label>
    <input type="number" name="phone_number" id="phone_number" class="form-control" dir="ltr" placeholder="09*********">
    <input type="hidden" name="handler" value="handler_post_sign_up">
    <div class="text-center mt-4">
        <input type="submit" class="btn btn-outline-success" value="ارسال">
    </div>
</form>

<?php
require_once "parts/footer.php";
?>


3- اسکریپت sign.up.complete.php

<?php
require_once "parts/header.php";
?>

<h1 class="text-center">تکمیل پروفایل</h1>

<form method="POST" class="col-6 m-auto">
    <label class="mb-2" for="username">نام کاربری ( کاراکتر های مجاز (a-z .) )</label>
    <input type="text" name="username" id="username" class="form-control" dir="ltr" placeholder="senior.x">

    <div class="w-100 mt-3"></div>

    <label class="mb-2" for="fullname">نام کامل</label>
    <input type="text" name="fullname" id="fullname" class="form-control" dir="rtl" placeholder="حسین باقری">
    
    <input type="hidden" name="handler" value="handler_post_sign_up_complete">

    <div class="text-center mt-4">
        <input type="submit" class="btn btn-outline-success" value="ارسال">
    </div>
</form>



<?php
require_once "parts/footer.php";
?>


4- اسکریپت view.php

<?php
require_once "parts/header.php";
?>

<h1 class="text-center">پروفایل <?= $GLOBALS['user_data']['fullname'] ?></h1>

<form method="POST" class="col-6 m-auto">
    <label class="mb-2" for="username">نام کاربری</label>
    <input type="text" disabled name="username" id="username" class="form-control" dir="ltr" placeholder="senior.x" value="<?= $GLOBALS['user_data']['username'] ?>">

    <div class="w-100 mt-3"></div>

    <label class="mb-2" for="fullname">نام کامل</label>
    <input type="text" disabled name="fullname" id="fullname" class="form-control" dir="rtl" placeholder="حسین باقری" value="<?= $GLOBALS['user_data']['fullname'] ?>">
    
    <input type="hidden" name="handler" value="handler_post_sign_up_complete">

</form>



<?php
require_once "parts/footer.php";
?>


5- اسکریپت توابع functions.php

<?php

require_once "misc.php";
require_once "db.php";

function generate_rand_int($from = 1000, $to = 9999)
{
    return random_int($from, $to);
}

function generate_otp_code($phone_number)
{
    $id = insertRow([
        "phone_number" => $phone_number,
        "code" => generate_rand_int(),
    ], "otp");

    $row = selectRow("otp", [$id], "id=?");
    $row = $row[0];

    return $row;
}

function getMessageOtpSent($otp)
{
    return "کد تایید : {$otp['code']}\nرپید کد";
}

function get_session_epxire_seconds()
{
    return 86400 * 30;
}

function generate_token($type, $user_id, $phone_number)
{
    $token = generate_password_hash(time() . uniqid("rpd_salt"));
    $id = insertRow([
        "user_id" => $user_id,
        "type" => $type,
        "token" => $token,
        "phone_number" => $phone_number
    ], "session_token");

    return [
        "id" => $id,
        "token" => $token
    ];
}

function getPostHandlers()
{
    return [
        "handler_post_sign_up",
        "handler_post_sign_up_verify",
        "handler_post_sign_up_complete"
    ];
}

function handler_post($post)
{
    $handler = $post['handler'] ?? "";

    $post_handlers = getPostHandlers();

    if (in_array($handler, $post_handlers)) {
        return $handler($post);
    } else {
        die("invalid handler {$handler}");
    }
}

function get_seconds_expire_otp()
{
    return 120;
}

function getResponseHtml()
{
    return [
        "status" => 0, // 0 -> fail , 1 -> success , -1 -> try again
        "message" => "",
        "data" => "",
        "redirect" => "",
    ];
}

function handler_post_sign_up($post)
{
    $phone_number = $post['phone_number'] ?? "";

    $seconds_expire = get_seconds_expire_otp();

    $res = getResponseHtml();

    if (regexCheck($phone_number, '/\d{11}/m')) {
        $old_phone_number = isRowExists("users", [$phone_number], "phone_number=?");
        if ($old_phone_number) {
            $res['message'] = "شماره {$phone_number} قبلا وارد شده";
        } else {

            $limit_message_cbk = function () use (&$limit_seconds, &$res) {
                $limit_seconds = get_seconds_expire_otp();
                $res['message'] = "از قبل کد فعالسازی برای شما فرستاده شده لطفا از زمان ارسال تا {$limit_seconds} ثانیه بعد اقدام کنید";
            };

            // check for sms limitation
            $can_send = limit_send_message(["restrict" => true, "column" => "phone_number", "data" => []], $post['phone_number']);

            if ($can_send) {
                $otp = generate_otp_code($post['phone_number']);
                $sms_res = send_message(getMessageOtpSent($otp), $post['phone_number'], ["column" => "phone_number", "data" => $otp]);

                if (is_int($sms_res)) {
                    $res['status'] = 1;
                    $res['message'] = "کد تایید به شماره {$post['phone_number']} ارسال گردید و در <span data-action=\"lesser\" data-on-end=\"reload\" class=\"timer\">{$seconds_expire}</span> ثانیه دیگر منقضی می گردد";
                    $res['data'] = $otp['id'];
                } else {
                    $limit_message_cbk();
                }
            } else {
                $limit_message_cbk();
            }
        }
    } else {
        $res['message'] = "شماره همراه نا معتبر";
    }


    return $res;
}

function handler_post_sign_up_verify($post)
{
    $res = getResponseHtml();

    $verify_code = $post['verify_code'];
    $id = $post['id'];

    $row = selectRow("otp", [$id], "id=?");

    if (!empty($row[0])) {
        $row = $row[0];
    }

    if (!$row) {
        $res['message'] = "موردی با این مشخصات یافت نشد";
    } else {
        $datetime = getDatetime();
        if (($row['expired_at'] < $datetime)) {
            $res['message'] = "کد وارد شده منقضی شده";
        } else {

            $attempted = $row["attempt"] + 1;
            $dataUpdate = ["attempt" => ($attempted)];

            $expire_otp = function () use (&$dataUpdate) {
                $dataUpdate['expired_at'] = getDatetime((time() - 10));
            };

            if ($row['code'] != $verify_code) {
                $res['message'] = "کاراکتر وارد شده مطابقت ندارد";
                $res['status'] = -1;
                $res['data'] = $id;

                if ($attempted == 3) {
                    $expire_otp();
                }

                $affectedRows = updateRow($dataUpdate, "otp", ["keys" => "id=?", "values" => [$id]]);
            } else if ($row['code'] == $verify_code) {
                $expire_otp();

                $dataUpdate['checked'] = true;
                updateRow($dataUpdate, "otp", ["keys" => "id=?", "values" => [$id]]);

                $token_data = generate_token("auth", -1, $row['phone_number']);
                setcookie("auth", $token_data['token'], (time() + get_session_epxire_seconds()));

                $res['status'] = 1;
                $res['message'] = "موفق , اکنون به صفحه پروفایل وارد می شید";
                $res['redirect'] = "sign.up.complete.php";
            }
        }
    }


    return $res;
}

function handler_post_sign_up_complete($post)
{

    $res = getResponseHtml();

    if (!@$post['username'] || !@$post['fullname']) {
        $res['message'] = "نام کاربری یا نام کامل وارد نشده";
    } else {
        $post['fullname'] = trim(strtolower($post['fullname']));
        $post['fullname'] = preg_replace('/\s{2,}/m' , " " , $post['fullname']);

        if (100 < mb_strlen($post['username']) || 100 < mb_strlen($post['fullname'])) {
            $res['message'] = "نام کاربری یا نام کامل بیشتر از 100 کاراکتر می باشد";
        } else {

            if (!regexCheck($post['username'], '/^[a-zA-Z.]{1,}$/m')) {
                $res['message'] = "نام کاربری معتبر نیست لطفا طبق الگو انتخاب کنید";
            } else {
                $row = isRowExists("users", [$post['username']], "username=?");

                if ($row) {
                    $res['message'] = "نام کاربری {$post['username']} از قبل انتخاب شده نام کاربری دیگری در  نظر بگیرید";
                } else {

                    $user_id = insertRow(["username" => $post['username'], "fullname" => $post['fullname'], "status" => "active", "phone_number" => $GLOBALS['user_data']['phone_number']], "users");
                    updateRow(["user_id" => $user_id], "session_token", ["keys" => "token = ?", "values" => [$_COOKIE['auth']]]);

                    header("Location: view.php");
                }
            }
        }
    }

    return $res;
}


6- اسکریپت توابع misc.php

<?php

function regexCheck($str, $regex)
{
    $str = preg_replace($regex,"", $str);
  
    return $str == "";
}

function limit_send_message($limit, $to)
{
    $can_send = 1;
    if ($limit) {
        $row = selectRow("otp", [$to, getDatetime()], "{$limit['column']}=? AND ? <= expired_at");
        if (isset($row[0])) {
            $row = $row[0];

            if (isset($limit['data']['id']) && $row['id'] != $limit['data']['id']) {
                $can_send = 0;
            } else if (!isset($limit['data']['id']) && !empty($limit['restrict'])) {
                $can_send = 0;
            }
        }
    }

    return $can_send;
}


function send_message($message, $to, $limit = ["column" => "", "data" => [], "restrict" => false])
{
    if (!empty($limit['column'])) {
        $status_limit = limit_send_message($limit, $to);
        if (!$status_limit) return $limit['column'];
    }

    $status = 0;

    $body = ["username" => 'USERNAME', "password" => 'PASSWORD', "to" => $to, "from" => 'FROM', "text" => $message, "isflash" => 'true',];
    $response = postRequest('http://api.payamak-panel.com/post/send.asmx/SendSimpleSMS', $body);

    $response = (array)new SimpleXMLElement($response);
    $response = trim($response['string']);

    if (100 < $response) $status = 1;
    else $status = 0;

    return $status;
}

function postRequest($url, $data)
{
    $params = http_build_query($data);
    $options = array(
        CURLOPT_URL => $url,
        CURLOPT_HTTPHEADER => ["Content-Type: application/x-www-form-urlencoded"],
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_POSTFIELDS => $params,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_RETURNTRANSFER => true
    );

    $ch = curl_init();
    curl_setopt_array($ch, $options);

    $response = curl_exec($ch);

    curl_close($ch);

    return $response;
}

function getDatetime($timestamp = null)
{
    $timestamp = $timestamp ?: time();
    return date("Y-m-d H:i:s", $timestamp);
}

function generate_password_hash($password, $option = ["cost" => "10"])
{
    return password_hash($password, PASSWORD_BCRYPT, $option);
}

function check_hashed_password($password, $hashed_password)
{
    return password_verify($password, $hashed_password);
}

function get_current_user_data()
{
    if (!isset($GLOBALS['user_data']))
        $GLOBALS['user_data'] = [];

    $status = false;

    if (isset($_COOKIE['auth'])) {
        $token_data = isRowExists("session_token", [$_COOKIE['auth']], "token=?");

        $invalid_cookie_cbk = function() use ($status){
            setcookie("auth" , "" , time() - 15);
            return $status;
        };

        if (!$token_data){
            return $invalid_cookie_cbk();
        }

        if ($token_data['expired_at'] < getDatetime()) {
            return $invalid_cookie_cbk();
        }


        $GLOBALS['user_data']['phone_number'] = $token_data['phone_number'];

        if ($token_data['user_id'] < 0) {
            $GLOBALS['user_data']['need_complete_profile'] = true;
        } else {
            $user = isRowExists("users", [$token_data['user_id']], "id=?");
            if ($user) {
                $GLOBALS['user_data']['need_complete_profile'] = false;
                $GLOBALS['user_data']['username'] = $user['username'];
                $GLOBALS['user_data']['fullname'] = $user['fullname'];
                $GLOBALS['user_data']['token'] = $token_data['token'];
                $status = 1;
            }
        }
    }

    return $status;
}


function get_current_user_authorize()
{
    $isUserLoggedInNeedCompleteProfile = !empty($GLOBALS['user_data']['need_complete_profile']);
    $isUserLoggedIn = isset($GLOBALS['user_data']['username']);

   
    

    $redirect = "";

    $php_script_name = basename($_SERVER['PHP_SELF']);

    if ($isUserLoggedInNeedCompleteProfile && $php_script_name != "sign.up.complete.php") {
        $redirect = "sign.up.complete.php";
    } else if (!$isUserLoggedIn && !$isUserLoggedInNeedCompleteProfile && $php_script_name != "sign.up.php") {
        $redirect = "sign.up.php";
    } else if($isUserLoggedIn && $php_script_name != "view.php"){
        $redirect = "view.php";
    }

    
    if ($redirect) {
       
        header("Location: {$redirect}");
    }
}


7- اسکریپت توابع db.php

<?php

function startMysql()
{
    $GLOBALS['mysqli'] = new mysqli("localhost", "root", "", "auth_users");
    if ($GLOBALS['mysqli']->connect_error) {
        die("MYSQL ISSUE : " . $GLOBALS['mysqli']->connect_error);
    }

    $GLOBALS['mysqli']->set_charset("utf8");
    $GLOBALS['stmt'] = $GLOBALS['mysqli']->stmt_init();
}

function endMysql()
{
    $GLOBALS['stmt']->close();
    $GLOBALS['mysqli']->close();
}

function insertRow($data, $table, $interface = null, $where = ["keys" => "", "values" => []])
{
    startMysql();

    $keys = array_keys($data);
    $keysStr = join(",", $keys);

    $values = array_values($data);

    if ($interface == "update") {
        $keysStr = "";
        foreach ($data as $theKey => $theValue) {
            $keysStr .= "{$theKey}=?, ";
        }

        $keysStr = substr($keysStr, 0, strlen($keysStr) - 2);

        if (count($where['values'])) {
            $values = array_merge($values, $where['values']);
            $where['keys'] = " WHERE " . $where['keys'];
        }

        $query = "UPDATE `{$table}` SET {$keysStr}" . $where['keys'];
    } else if ($interface === null) {

        $valuesStrQuestion = str_repeat("? , ", count($values));

        if (1 < count($values)) {
            $valuesStrQuestion = substr($valuesStrQuestion, 0, strlen($valuesStrQuestion) - 3);
        }

        $query = "INSERT INTO `{$table}` ({$keysStr}) VALUES ({$valuesStrQuestion})";
    }

    $inserted_id = 0;
    $GLOBALS['stmt']->prepare($query);
    $GLOBALS['stmt']->bind_param(str_repeat("s", count($values)), ...$values);

    if ($GLOBALS['stmt']->execute()) {
        $inserted_id = $GLOBALS['stmt']->affected_rows;

        if ($interface === null) {
            $inserted_id = $GLOBALS['stmt']->insert_id;
        }
    }

    endMysql();

    return $inserted_id;
}

function selectRow($table, $data = [], $concat_query = "1=1")
{
    startMysql();

    $query = "SELECT * FROM `{$table}` WHERE {$concat_query}";
    $rows = [];

    $GLOBALS['stmt']->prepare($query);
    if ($data) {
        $GLOBALS['stmt']->bind_param(str_repeat("s", count($data)), ...$data);
    }

    if ($GLOBALS['stmt']->execute() && $res = $GLOBALS['stmt']->get_result()) {
        if ($GLOBALS['stmt']->affected_rows || $res->num_rows) {
            while ($row = $res->fetch_assoc()) {
                $rows[] = $row;
            }
        }
    }

    endMysql();

    return $rows;
}

function updateRow($data, $table, $where = ["keys" => "", "values" => []])
{
    $affectefRows = insertRow($data, $table, "update", $where);
    return $affectefRows;
}

function isRowExists($table, $data, $concat_query)
{
    $rows = selectRow($table, $data, $concat_query);

    if (!$rows) return false;
    else {
        return $rows[0];
    }
}


قبل از استفاده از سورس :

  1. وارد پوشه to import شوید و دیتابیس را وارد کنید .
  2. اطلاعات ورود سامانه پیامکی در تابع send_message در اسکریپت misc.php را وارد نمایید


دانلود سورس پروژه تایید شماره موبایل PHP

لینک مقاله

ارسال نظر

جهت استفاده از کد حتما از تگ pre استفاده نمایید .

contact us