آموزش jwt در php با مثال عملی

داشتم یک API برای خودم می‌نوشتم و نیاز داشتم دسترسی آن را محدود کنم. با چیزی به نام jwt آشنا شدم. بعد از دو روز سر و کله زدن و آزمون و خطای با روش های مختلف بالاخره راه ساده و درست استفاده ازاین jwt را پیدا کردم.

در این مطلب به صورت کامل توضیح می‌دهم که jwt چیست و چه کار می‌کند و یک مثال عملی هم در استفاده از آن در api هایی با استفاده از php می‌نویسیم خواهم زد.

برای مثال یک API خیلی ساده درست کردم که برای احراز هویت و دسترسی دیگران به این API با jwt محدودیت ایجاد می‌کنم.

jwt چیست؟

jwt مخفف Json Web Token است. یعنی توکن‌هایی که به صورت یک رشته json هستند.

قبل از اینکه جلوتر از این برویم بگذارید ببینیم اصلا توکن یعنی چه؟

توکن، چیزی مثل کارت شناسایی

توکن را کلید ورود به خانه یا کارت ملی برای انجام دادن کارهای اداری و بانکی در نظر بگیرید. برای اینکه در بانک کار شما را انجام بدهند از شما کارت ملی می‌خواهند. همین کارت هم تمام کار احراز هویت شما را انجام می‌دهد و شما دسترسی به حساب خود خواهید داشت.

در دنیای امنیت نرم افزار هم توکن یک کد است که برای دسترسی دادن به یک بخش محافظت شده از نرم افزار از آن استفاده می‌شود.

در مثال ما قرار است یک API داشته باشیم که دسترسی به آن فقط برای دارندگان توکن مجاز است.

JWT یک توکن به صورت رشته JSON

در این بحث ما توکنی که داریم از آن حرف می‌زنیم به صورت یک رشته json است (با json که حتما آشنا هستید؟).

در این توکن اطلاعات کاربری که توکن به نامش صادر شده و یک سری چیزهای دیگر که درباره آن حرف می‌زنیم در آن وجود دارد.

JWT چه شکلی است؟

تمام jwt ها چنین ساختار سه بخشی دارند که با نقطه از همدیگر جدا می‌شوند:

Header.Payload.Signature

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsImF1ZCI6Imh0dHA6XC9cL2V4YW1wbGUuY29tIiwiaWF0IjoxMzU2OTk5NTI0LCJuYmYiOjEzNTcwMDAwMDAsImRhdGEiOnsiaWQiOiI5IiwiZmlyc3RuYW1lIjoiTWlrZSIsImxhc3RuYW1lIjoiRGFsaXNheSIsImVtYWlsIjoibWlrZUBjb2Rlb2ZhbmluamEuY29tIn19.h_Q4gJ3epcpwdwNCNCYxtiKdXsN34W9MEjxZ7sx21Vs

JWT چطور کار می‌کند؟

سمت سرور یک توکن یا همان jwt ایجاد میشود که داخل آن اطلاعات هویتی کاربر (مثل نام کاربری یا id کاربر در دیتابیس یا هرچیزی که شما به عنوان برنامه نویس نیاز داشته باشید) به صورت کد گذاری شده قرار داده شده است.

بعد از ایجاد شدن، این توکن برای کاربر فرستاده می‌شود. کاربر به همراه ریکوئست‌هایی که به سمت سرور برای استفاده از API می فرستد، توکن jwt را هم باید بفرستد. تا سرور متوجه شود که این کاربر تایید شده است و به او اجازه دسترسی به api بدهد.

هر کدام از قسمت های JWT چه کاری انجام میدهند؟

گفتیم هر jwt سه بخش دارد ببینیم هر کدام از اینها چه هستند.

Header

header یک رشته json است که داخل آن اطلاعات مربوط به الگوریتم رمزگذاری قرار داده می‌شود.

یک مثال از header:

$header = '{
"typ":"JWT",
 "alg":"HS256"
}';

در مثال بالا دو قسمت وجود دارد:

typ: که مشحص میکند این مربوط به JWT است.

alg: که الگوریتم رمزگذاری را تعیین کرده.

Payload

این هم یک رشته جیسون است که داخل آن هر اطلاعاتی که بخواهید می‌توانید جا دهید: مثلا ID کاربر در دیتابیس یا اسم کاربر و هر اطلاعات دیگری.

فقط حواستان باشد با اینکه این رشته رمزگذاری می‌شود اما اطلاعات حساس مثل ایمیل یا رمز عبور را اینجا نگذارید. و فقط چیزهایی که برای احراز هویت ضروری است را قرار دهید.

معمولا به صورت استاندارد یک سری مقادیر وجود دارد که آنها را می‌توان به Payload اضافه کرد اما الزامی نیستند.

مثال یک Payload

$Payload = '{
"user_id":"5",
"name" : "reza",
"exp" : "2020-30-12 00:00:00"
}'

عبارت های user_id و name چیزهایی است که من برای احراز هویت کاربر نیاز دارم و exp مقداری است که تعیین کننده تاریخ انقضا استفاده از این توکن است یعنی از این تاریخ به بعد دیگر این توکن معتبر نیست.

به Payload ادعا(Claim) هم می‌گویند. چون در واقع با فرستادن توکن کاربر ادعا می‌کند هویت من این چیزی است که در playload نوشته شده. حالا باید نرم افزار این هویت را تایید یا رد کنید.

Singnature

از اسمش معلوم است یعنی امضا.

همانطور که هر شخصی امضای مخصوص به خودش را دارد امضای هر توکن jwt هم منحصر به فرد است.

اینجا هم ما باید یک رشته رمز گذاری شده درست کنیم و در توکن جا بدهیم تا فقط خودمان بتوانیم درستی یا نادرستی توکن را تشخیص بدهیم.

امضا چگونه درست میشود؟

برای درست کردن امضا باید قسمت Header و Payload را با هم ترکیب کنیم و هر کدام را با همان الگوریتمی که برای Header استفاده کردیم رمزگذاری کنیم:

base64($header) . "." . base64($Payload)

اینجا هر دو قسمت را بعد از رمز گذاری با یک نقطه کنار هم گذاشته‌ام.

و در آخر یک کلید امنیتی(Secret Key) درست می‌کنیم و با این کد این قسمت را رمز گذاری می‌کنیم.

کد امنیتی را هیچ کس نباید غیر از خودتان داشته باشد. بهتر است آن را در یک فایل config ذخیره کنید و در موقع استفاده آن را بخوانید.

با همین کار قسمت Signature ساخته می‌شود.

چطور به صورت عملی از jwt در php استفاده کنیم؟

وقتش رسیده که یک مثال عملی با jwt انجام بدهیم تا دقیق نحوه کار آن را ببینیم.

اینجا من یک API ساده درست میکنم و کاری میکنم که فقط با استفاده از jwt احراز هویت و دسترسی به API امکان پذیر باشد.

یک پوشه برای این پروژه به نام api_example درست کرده‌ام.

یک سری اطلاعات هم در دیتابیس دارم که برای دسترسی دیگران به این ها api درست می‌کنم. این اطلاعات داخل یک جدول به نام users هست که تمام کاربرهای من آنجا ثبت شده‌اند.

آموزش jwt
اطلاعات کاربران برای ساخت api

یک پوشه دیگر درون پروژه‌ام دارم به اسم api و داخل آن فایلی گذاشته ام به نام users.php درون این پوشه کدهای api من قرار دارند.

قرار است هر کسی دسترسی به api من داشت بتواند ایمیل کاربرها را ببیند. خب قطعا جون این اطلاعات خیلی حساس است پس برای دسترسی دادن باید از یک روش با امنبت بالا مثل jwt استفاده کنیم.

داخل users.php هم ایدی اسم وایمیل کاربرها را به صورت json نشان می‌دهم.

این هم کدهای فایل users.php :

<?php
//Api for Accessing users data

//connection to database
$db= new mysqli('localhost','root','','api_example');

//sql query for reading users data from database
$sql = "SELECT id,name,email FROM `users`";

//run query and sace data into variable
$result = $db->query($sql);
//get results from db 
$result = $result->fetch_all(MYSQLI_ASSOC);

//set headers for browser to understand this is json format
header("Access-Control-Allow-Origin: *");
header('Content-Type: application/json; charset=UTF-8');

//convert array to json

$users_data_json = json_encode($result,1);

//present data;
echo $users_data_json;

الان اکر این آدرس را در مرورگر وارد کنید میبینید که لیست کاربرهای من با فرمت json نشان داده می‌شود.

http://localhost/api_example/api/users.php

یک مثال از api

اما خب این API کاملا باز است وهر کسی با زدن این url به آن دسترسی دارد. من هیچ احراز هویتی انجام ندادم.

حالا می‌رسیم به بخش اصلی و مهم ساخت توکن jwt.

ساختن توکن JWT

قبل از هر چیز من در روت پروژه یک فایل index.php درست می‌کنم و در این فایل فرمی برای درخواست API می‌سازم. تا کاربری که وارد سایت شد نام کاربری خودش را وارد کند و با کلیک روی دکمه درخواست، کد مخصوص خودش را دریافت کند.

کدهای فایل index.php :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

<form action="api/jwt-create.php" method="post">
<label for="name">نام کاربری</label>
<input type="text" name="name" id="name">
<input type="submit" value="درخواست api">

</form>
</body>
</html>



فرم درخواست api

وقتی کاربر نام کاربری را وارد کرد و دکمه درخواست را زد، به فایل jwt-create.php ارسال می‌شود. آنجا این اسم را دریافت می‌کنیم و کار ساختن کد jwt را انجام می‌دهیم. پس باید در پوشه API یک فایل هم به نام jwt-create.php درست کنیم تا در این فایل کارهای ساخت توکن را انجام بدهیم.

برای ساختن jwt از یک کتابخانه معروف php به نام php-jwt استفاده می‌کنیم تا کار رمزگذاری و رمزگشایی توکن‌ها برای ما راحت‌تر شود.

من برای وارد کردن این کتابخانه در پروژه از ابزار composer استفاده می‌کنم.

کافی است این دستور را در خط فرمان IDE وارد کنید(البته کامپوزر باید روی سیستم نصب باشد).

composer require firebase/php-jwt

بعد هم فایل autoload.php را در jwt-create.php لود کنید و همینطور namespace این کتابخانه را در صفحه استفاده کنید.

این شکلی:

<?php
//here we create jwt for accessing others to our api
include '../vendor/autoload.php';

//use php-jwt namespace
use \Firebase\JWT\JWT;

یادتان هست گفتیم jwt سه بخش دارد به اسم Header، Paylod و Signature. اینجا باید این سه بخش را درست کنیم.

کتابخانه php-jwt کار ما را راحت می‌کند. فقط یک آرایه به عنوان payload و یک کلید امنیتی از ما دریافت می‌کند و کار ساخت هر سه بخش و رمز گذاری آنها را برای ما انجام می‌دهد.

قبل از هر چیز نام کاربری که کاربر ارسال کرده را اینجا می‌گیریم و در یک متغیر ذخیره می‌کنیم:

$user_name = $_POST['name'];

ساخت کلید امنیتی

در مرحله بعد باید یک secret key یا کلید امنیتی برای خودمان تعریف کنیم.

این کلید بهتر است یک رشته طولانی باشد و دست هیچ کسی غیر از خودمان نباشد.

قاعدتا این کلید نباید مستقیم در سورس کد ما جا بگیرد و بهتر است آن را در یک فایل config جدا گانه قرار بدهیم. من اینجا برای نشان دادن در همین فایل گذاشته‌ام اما این کار از لحاظ امنیتی کار درستی نیست.

$secret_key = "SECRET_KEY";

بعد باید قسمت payload را بسازیم. بقیه کارها را خود کتابخانه php-jwt برای ما انجام می‌دهد.

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

در این مثال چون قرار است کد jwt برای هر نام کاربری متفاوت باشد من در این قسمت نام کاربری را به آن اضافه می‌کنم.

برای اضافه کردن دیتا به payload بهتر است اطلاعات را درون یک آرایه قرار دهید.

من فقط نام کاربری را گذاشته‌ام شما میتوانید id کاربر در دیتابیس یا اطلاعت دیگر را هم قرار دهید اما سعی کنید تعدادشان زیاد نشود. ضمنا متغیرهای دیگر را کامنت می‌کنم چون برای ساده سازی کار نمی‌خواهم از آنها استفاده کنم اما شما میتوانید هرکدام را خواستید قرار دهید.

$payload = array(
    // "iss" => "http://example.org",
    // "iat" => time()
    // "nbf" => time() + 10,
    // "exp" => time() + 3600
    $data = array(
        'name' => $user_name
    )
);

متغیرهای پیش فرض jwt:

iss: یک رشته که مشخص کننده ارسال کننده ریکوئست است میتواند دامنه یا نام اپلیکیشن یا هرچیزی که مشخصه فرد ارسال کننده ریکوئست است باشد.

iat: مشخص کننده زمانی است که توکن صادر شده است.

nbf: زمانی را تعیین میکند که قبل از آن استفاده از توکن امکان پذیر نیست.

exp: تاریخ انقضا توکن.

در آخر برای ساخت jwt از تابع encode خود کتابخانه php-jwt استفاده می‌کنیم و آن را درون یک متغیر می‌گذاریم.

$jwt = JWT::encode($payload, $secret_key);

پارامتر اول باید payload باشد و دومی هم کلید امنیتی که اول کار ساخته بودیم بقیه کارها مثل ساخت هدر و امضا را php-jwt برایمان انجام می‌دهد.

الان توکن ما در متغیر jwt$ قرار دارد. اینجا می‌توانیم به کاربر نمایش دهیم:

echo "توکن شما با موفقیت ساخته شد" . "</br>";
echo "<pre><samp>" . $jwt . "</samp></pre>";

تمام کدهای فایل jwt-create.php :

<?php
//here we create jwt for accessign others to our api
include '../vendor/autoload.php';

use \Firebase\JWT\JWT;

$user_name = $_POST['name'];

//set a powerful encoded secret key
// $secret_key = hash_hmac('ripemd160', 'secret key must not be available for everyone', 'secret');
$secret_key = "rezasalam";

//se data for putting in token
$payload = array(
     // "iss" => "http://example.org",
    // "aud" => "http://example.com",
    // "iat" => 1356999524,
    // "nbf" => 1357000000,
    $data = array(
    'name' => $user_name
    )
);

//create jwt 
$jwt = JWT::encode($payload, $secret_key,'HS256');

//print the created token for user
echo "توکن شما با موفقیت ساخته شد" . "</br>";

echo "<pre><code>" . $jwt . "</code></pre></br>";
echo $secret_key;

با انجام این مرحله دیگر عملیات ساخت توکن برای کاربرها به درستی انجام می‌شود.

واضح و منطقی است که در یک برنامه واقعی من برای هر کسی توکن صادر نمی‌کنم. پس باید قبل از صدور توکن چک کنم که این نام کاربری مورد تایید من هست یا خیر. من برای اینکه مثال ساده باشد و فقط اصل کار را بیان کنم از نوشتن موارد این چنینی صرف نظر کرده‌ام.

الان باید کاری کنیم قبل از ارائه API به کاربر صحت توکن jwt چک شود.

اعتبار سنجی توکن JWT قبل از ارائه API به کاربر

وارد فایل users.php می‌شویم. و در این فایل قبل از کدهایی که برای api نوشته بودیم باید کار چک کردن صحت توکن را انجام بدهیم.

قبل از هر چیز فایل autoload.php را اینجا هم include می‌کنیم و namespace کتابخانه php-jwt را وارد می‌کنیم. تا بتوانیم برای رمزگشایی توکن از این کتابخانه استفاده کنیم.

include '../vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

اول چک می‌کنیم که آیا کاربری که به این صفحه ریکوئست داده، توکن خودش را وارد کرده و اگر وارد کرده، توکن او صحیح است یا خیر.

پس دیتایی که کاربر به همراه ریکوئست فرستاده را دریافت می‌کنیم.

این شکلی:

$data = json_decode(file_get_contents("php://input"));

file_get_contents("php://input") هر دیتایی که کاربر با هر متدی فرستاده است را دریافت می‌کند.

چون دیتا به صورت جیسون فرستاده می‌شود اینجا با تابع json_decode آن را به آرایه تبدیل می‌کنیم ودر data$ می‌گذاریم.

کلید امنیتی را هم که نوشته بودیم دوباره می‌آوریم.

$secret_key = "SECRET_KEY";

نکته: باید در داکیومنت API خودتان بنویسید که استاندارد دریافت این کد چطور است و کاربر درخواست کننده در چه کلیدی این کد را بفرستد من اینجا فرض می‌کنم که کاربر آن را با فرمت JSON و در کلید به نام jwt می فرستد.

متد ارسال ریکوئست هم اینجا باید post باشد چون قرار است یک توکن امنیتی فرستاده شود فرستادن به صورت GET امن نیست.

پس الان باید چک کنیم که jwt وجود دارد یا نه و بعد آن را رمزگشایی کنیم و چک کنیم که اطلاعات داخل آن صحیح هست یا خیر.

توکنی که از ریکوئست گرفتیم را در متغیری به نام $jwt می‌گذاریم.

$jwt=isset($data->jwt) ? $data->jwt : "";

کد بالا چک میکند که در دیتای ارسال شده کلید jwt وجود دارد یا خیر اگر وجود داشت خودش را می‌گذارد و اگر نبود یک رشته خالی برمیگرداند.

در مرحله بعد چک می‌کنیم که اگر مقدار jwt موجود بود کد توکن را رمزگشایی کند با متد decoded:

if($jwt){

    try{
        $decoded = JWT::decode($jwt,$secret_key, new Key($key, 'HS256'));
        http_response_code(200);
        
    }
    catch(Exception $e){
        http_response_code(401);
 
        // show error message
        echo json_encode(array(
            "message" => "Access denied.",
            "error" => $e->getMessage()
        ));
    }



    
    
}else{
    header('HTTP/1.0 401 Unauthorized');
    echo json_encode(array(
        "message" => "شما دسترسی به این صفحه ندارید"
    ));

    die();
}

کد بالا اول خالی نبودن متغیر jwt را چک می‌کند اگر خالی بود یک هدر 401 و پیغام خطا برمیگرداند.

اگر خالی نبود jwt را رمزگشایی می‌کند

برای رمزگشایی از متد JWT::decode استفاده میشود که سه پارامتر میگیرد کد توکن که از کاربر دریافت کردیم، secret key و پارامتر سوم هم الگوریتم رمزگذاری که با توجه به داکیومنت کتابخانه jwt همین array('HS256') را برای آن می‌نویسیم.

اگر رمز گشایی موفقیت آمیز باشد کد ادامه پیدا میکند و ما با http_response_code(200) به مرورگر می‌فهمانیم که همه چیز درست بوده است. دیگر کد وارد else نمی‌شود و در ادامه API به کاربر نمایش داده می‌شود.

اما اگر رمزگشایی با موفقیت انجام نشود خود کتابخانه php-jwt یک خطا برمی‌گرداند که اینجا در کد با استفاده از try catch خطا گرفته می‌شود و به کاربر نمایش داده می‌شود.

خب تا این مرحله هم قسمت ساخت توکن درست شد و هم بررسی توکن برای دسترسی دادن به api.

فقط می‌ماند تست کردن هر دو اینها.

اول برویم و به عنوان کاربر این سایت یک توکن دریافت کنیم.

وارد صفحه اول سایت می‌شویم

localhost/api-example

چون فایل index.php وجود دارد مرورگر مستقیم وارد این صفحه می‌شود تا فرم درخواست api را ببنیم.

من یک نام کاربری اینجا وارد می‌کنم.

توکن jwt
فرم درخواست api برای کاربران

با زدن دکمه درخواست به صفحه‌ای منتقل می‌شویم و کد توکن jwt به ما نمایش داده می‌شود.

توکن ساخته شده برای کاربر

الان برویم امتحان کنیم با این توکن می‌توانیم به api دسترسی داشته باشیم یا نه.

برای تست با استفاده از توکن اینجا باید از نرم‌افزار postman استفاده کنیم. postman نرم افزاری است که کار ارسال ریکوئست را برای ما می‌تواند شبیه سازی کند.

نکته: برای ریکوئست دادن به api از طریق php باید از curl یا کتابخانه های مرتبط مثل guzzlehttp استفاده کرد اما اینجا چون فقط می‌خواهیم درست کار کردن jwt را امتحان کنیم نیازی به ساختن یک صفحه php و کار با curl نیست.همین postman جواب کار ما را خواهد داد. اما اگر کسی بخواهد از سایت یا نرم افزار خودش برای api ما ریکوئست بفرستد باید از curl استفاده کند.

وارد postman می‌شویم. یک قسمت آدرس دارد که آدرس صفحه users.php را وارد میکنیم و متد را هم باید POST بگذاریم چون قرار است توکن jwt را به همراه ریکوئست بفرستیم.

بعد باید به همراه ریکوئست توکن را هم قرار دهیم برای این کار از قسمت Body گزینه raw را انتخاب میکنیم و نوع داده را هم JSON می‌گذاریم.

در کادر پایین به صورت json کد توکن را که قبل تر دریافت کرده بودیم با کلید jwt قرار می‌دهیم. این شکلی:

در آخر روی Send کلیک میکنیم تا ریکوئست ارسال شود.

نتیجه ریکوئست api

مشاهده کردید که چون توکنی که دریافت کرده بودیم را دقیق و درست ارسال کردیم احراز هویت انجام شد و نتیجه نشان داده شد.

اگر دقت کرده باشید در فرایند چک کردن jwt هیچ کجا من هیچ دیتایی را از دیتابیس نخواندم که بخواهم با آن چک کنم چون خود jwt هر چه نیاز دارم را در خودش قرار داده. به خاطر همین اصطلاحا میگویند jwt خودشمول یا self contained است.

موقع ساخت توکن نام کاربری را در payload قرار دادم چون نام کاربری منحصر به فرد است پس توکن ساخته شده هم با بقیه توکن‌ها متفاوت خواهد بود.

هیچ کس دیگری هم نمی‌تواند یک توکن مثل این را بسازد حتی اگر نام کاربری را داشته باشد چون کلید امنیتی من را ندارد باز هم این کار غیر ممکن است. برای همین secret key خیلی مهم است و نباید هیچ کس از آن مطلع شود.

جمع بندی

jwt یکی از امن ترین روش ها برای دسترسی دادن به api هاست. برای همین در مواردی که نیاز به محدود کردن دسترسی و احراز هویت دقیق در API داریم از این روش استفاده میکنیم.

کتابخانه php-jwt تمام کارهای رمزگذاری و رمزگشایی را برای ساخت توکن jwt انجام می‌دهد فقط ما باید قسمت payload را به آن بدهیم.

کوچکترین تغییر داخل هر کدام از بخش های این توکن کل توکن را بی‌اعتبار می‌کند.

سعی کردم تمام چیزی که یادگرفته بودم و خودم پیاده سازی کردم را اینجا به اشتراک بگذارم.

البته jwt کاربردهای دیگری مثل احراز هویت کاربران برای ثبت نام در سایت هم دارد اما بیشتر در api ها استفاده می‌شود.

30 دیدگاه دربارهٔ «آموزش jwt در php با مثال عملی»

  1. سلام.باتوجه به آموزشی که بالا دادید.این روش من میتونه پاسخ بگیرد.

    من یک اسکریپت طراحی کردم برای وبلاگ های که قابلیت مهمی مثل آمار کاربران و افراد آنلاین در هر صفحه و ارتباط کاربران آنلاین بایکدیگر. و قصد دارم که برای ارتباط به سایت و دریافت اطلاعات از توکن استفاده کنم که کاربر از قبل برای دریافت این قابلیت در سایت ایجاد کرده و میخواهم بررسی شود که این درخواست از چه صفحه ای ارسال شده و مراحل اعتبارسنجی توکن انجام شود وپاسخ داده شود.
    مهم ترین بخش اینجاست این درخواست به صورت GET باید ارسال شود چون هر کاربر قرار است صفحه را در تگ اسکریپت در وبلاگش قرار دهد.و بعد از اجرا هر بار صفحه اعتبارسنجی و پاسخ داده شود.و زمانی که اسکریپت اجرا شود نام دامنه را بصورت post ارسال کند اگر اون دامنه ثبت بود پاسخ داده شود.
    یعنی فقط درخواست از این دامنه قابل پاسخ است.اگر بصورت GET ارسال شود توکن قابل شناسایی می باشد و میتونه قابل استفاده هکر ها و … قرار گیرد و اطلاعات را واکشی کنند.حتی اگر بصورت پست ارسال شود در کد اسکریپت قابل مشاهده است.چه راهی پیشنهاد می دهید.

    1. سلام
      به طور کلی توکن نباید از سمت فرانت ارسال بشه. اما وقتی قراره اسکریپتتون توی تگ اسکریپت قرار بگیره چاره ای نیست.
      اینکه چک کنید که این درخواست از سمت چه دامنه‌ای ارسال شده هم به راحتی قابل دستکاریه.
      اگر حتما نیازه که از سمت فرانت ریکوئست ارسال بشه، یک نگاه به این سایت بندازید:
      https://obfuscator.io/
      راجع بهش سرچ کنید. این ابزار کد جاوااسکریپت شما رو به شکل ناخوانا تبدیل میکنه.
      اما باز هم با این روش کاملا امن نمیشه.

  2. خدا پدر و مادرتو بیامرزه که انقدر ساده و روان توضیح دادی. پدرم درومد از بس مطالب این سایتهارو بلغور کردم و آخرش نفهمیدم چی شد

  3. محمود رنجبر نورآبادی

    سلام من میخوام از این روش برای لاگین به وبسایت استفاده کنم این برنامه آخری postman رو هم باید نصب کنم یا بدون برنامه هم میتونم باهاش کار کنم ؟

    1. سلام
      بله میتونید.
      من از postman فقط برای تست api و توکن استفاده کردم.
      در یک نرم افزار واقعی، اصلا نیازی به postman نیست و ارسال ریکوئست با کد، داخل خود نرم افزار انجام میشه.
      معمولا در php با کتابخانه guzzlehttp این کار رو انجام میدن.

  4. سلام دوباره رضای عزیز تو سیستم لاگین وقتی کاربر نام کاربری و رمز عبور رو وارد میکنه و تایید میشه کاربر سایت هست
    ما براش توکن رو میسازیم بعدش این توکن رو کجا ذخیره کنیم تو کوکی ؟
    من همین رو متوجه نمیشم که بعد از ساخت این توکن خودش جایی ذخیره میشه یا باید ذخیرش کنیم ؟

    و اینکه یه مثال در این خصوص یعنی لاگین کاربر میتونید ایمیل کنید برام یا
    آدرس هاست رو بدم میتونید سیستم لاگینش رو به این روش تغییر بدین
    خیلی مهمه یه راهنمایی جامع کنید که من درستش کنم

  5. محمود رنجبر نورآبادی

    سلام مجدد بعد از اینکه توکن رو در دیتابیس ذخیره کردیم و کاربر نام کاربری و رمز عبور رو درست وارد کرد چیکار کنیم راهنمایی لطفا

  6. سلام خطای زیر رو دریافت میکنم در درخواست لیست کاربران با پست من ، چطوری مشکلو حل کنم؟:
    Warning: foreach() argument must be of type array|object, string given in D:\…\jwt\vendor\firebase\php-jwt\src\JWT.php on line 428

    1. رضا محمره

      سلام.
      باید ببینم دقیقا چه کدی نوشتی اما از روی خطایی که میده احتمال میدم یک جایی از foreach استفاده کردی و مقدار داخلش آرایه و آبجکت نیست.
      یا اینکه متغیری که داری به عنوان لیست کاربرها برای api برمیگردونی یا echo می کنی، آرایه یا آبجکت نیست.

  7. سلام.
    ممنون از آموزش خوبتون.
    فقط یه نکته : در فایل jwt-create.php شما برای ساخت jwt از دو قسمت استفاده کردید که باعث میشه ارور دریافت کنیم و نیاز یه اصلاح داره.
    jwt = JWT::encode($payload, $secret_key); ==> $jwt = JWT::encode( $payload , $secret_key, “HS384” )$;

    1. سلام
      فکر کنم منظورت اینه که چطوری با curl ریکوئست بزنی و در پاسخ یک توکن دریافت کنی.(اگر منظورت این نیست بنویس تا دقیق تر پاسخ بدم)

      اول از همه باید یک url یا همون endpoint داشته باشیم که اون url در پاسخ یک توکن به ما تحویل بده.
      اینکه چطور با curl کار کنیم رو اینجا نمیشه نوشت. خودش یک مطلب جدا نیاز داره اما خیلی هم سخت و پیچیده نیست
      مثال های این لینک که میذارم رو ببینی، فکر کنم راحت روش کار رو متوجه بشی.
      https://weichie.com/blog/curl-api-calls-with-php/
      در کل:
      1-باید یک ریکوئست از نوع post به یک url ارسال کنی که همراه این ریکوئست اطلاعات برای احراز هویت وجود داشته باشه
      2-اون url اطلاعات رو دریافت کنه و بر اساس اونها یک توکن تولید کنه و در response تحویل بده.

      1. ممنون اقای محمره .
        مشکلم با هاست حل شد.
        نیازی به curl یا guzzlehttp هم نبود.
        باز هم ممنون بابت اموزش خوبتون.

  8. سلام .
    ببینید من تمام کارهایی که بالا گفتید انجام دادم و توکن تولید شد.
    ولی چون از postman استفاده نمیکنم و با xampp لوکال کار میکنم قسمتی که میخوام توکن دریافت کنم که اجازه دسترسی به api بده نمیتونم دریافت کنم و عملا توکن NULL هست.
    به صورتی دستی وقتی توکن وارد میکنم انجام میشه مثل مثال زیر:

    require_once ‘vendor\autoload.php’;
    use Firebase\JWT\JWT;
    use Firebase\JWT\key;
    try {

    $jwt = “eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJpc3MiOiJodHRw”;

    $secret_key = “ELITE_”;

    $json_data = JWT::decode($jwt,new key($secret_key,”HS384″));
    $json_data = (array) $json_data;
    $json_data[‘data’] = (array) $json_data[‘data’];

    $stmt = $db->prepare(“SELECT * FROM user WHERE id_phone = :userId”);
    $stmt->bindParam(“:userId”, $json_data[‘data’][‘userId’]);
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);

    echo json_encode($result , JSON_HEX_TAG | JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_AMP);

    } catch (Exception $e) {
    echo $e->getMessage();
    }

    1: الان واسه دریافت توکن من باید دقیقا چیکار کنم روی لوکال یا هاست؟
    2: نیاز هست از curl استفاده کنم که هم دوباره توکن با curl تولید کنم و همینطور دریافت کنم؟
    3: از کتابخانه guzzlehttp واسه دریافت توکن استفاده کنم که البته رو php ورژن 8.1 خطا میده؟

  9. سلام اقای محمره.
    من از طریق هدر Bearer بخام توکن دریافت کنم به چه صورت میشه؟
    تو روش بالا که توضیح دادید توکن داخل بادی گذاشتید. تو روش هدر , من توکن تو قسمت Authorization قسمت Bearer token گذاشتم ولی خطا میده.

    if($_SERVER[“REQUEST_METHOD”] == “GET”){

    try{
    $allheaders=getallheaders();
    $jwt=$allheaders[‘Authorization’];

    $secret_key = “fggdgdg”;
    $user_data=JWT::decode($jwt,$secret_key,array(‘HS256’));
    $data=$user_data->data;
    echo json_encode([
    ‘status’ => 1,
    ‘message’ => $data,
    ]);
    }catch(Exception $e){
    echo json_encode([
    ‘status’ => 0,
    ‘message’ => $e->getMessage(),
    ]);
    }
    }else {
    echo json_encode([
    ‘status’ => 0,
    ‘message’ => ‘Access Denied’,
    ]);
    }

    خطا ====>
    {
    “message”: “Access denied.”,
    “error”: “Unexpected control character found”
    }

    1. سلام وقتی از header قسمت authorization رو می گیری به همراه bearer یک عبارت اضافی “Bearer” وجود داره باید این مقدار رو ازش جدا کنی تا فقط توکن باقی بمونه من با این روش این کار رو کردم:

      $bearer= $all_headers['Authorization'];
      $jwt =trim(str_replace('Bearer','',$bearer));

      یک نکته دیگه، الان که داشتم این رو تست می کردم دیدم کتابخونه firebase/php-jwt در نسخه جدیدش روش decode کردن رو تغییر داده توی مقاله آپدیتش کردم. یک نگاهی به مستنداتش هم بندازی خوبه

  10. سلام،
    زمانی که توی اکسپایر تایم قرار میگیره بعدش توکن دیگه کار نمیکنه، و باید دوباره کاربر لاگین کنه.
    چطوری میتونیم این زمان رو شارژ کنیم؟ یعنی اگه کاربر داره با سیستم کار میکنه اکسپایر تایمش شارژ بشه.

  11. سلام. ممنون از شما.
    فقط یک چیزی رو خوب نفهمیدم که حالت غیر امنش چیه که ما از این توکن استفاده میکنیم.
    مثلا فکر کنید من اطلاعات یک کاربر مثل رمز عبور و ای دی اون رو دارم و به سرور ارسال میکنم، خوب سرور برای من یک توکن میسازه و من توکن رو دوباره برای سرور ارسال میکنم. و خوب اینطوری که من فهمیدم به اطلاعات دسترسی دارم دیگه این کجاش امن شد؟ (اینجا رو نفهمیدم)

    1. سلام
      وقتی نرم افزار از فریمورک های فرانت اند مثل ری اکت یا ویو استفاده می کنه و این توکن در فرانت ذخیره میشه (مثلا داخل local storage) میتونه مورد حمله xss قرار بگیره.

  12. سلام مهندس محمره وقت بخیر
    ممنون از آموزش مفیدتون
    سوالی که دارم اینه تا چه زمانی این توکن معتبر هست؟
    چطور میشه این توکن رو رفرش کرد؟
    اگه توکن تاریخش بگذره نیاز به ارسال مجدد نام کاربری و کلمه عبور هست؟
    چطور میتونیم برای امنیت بیشتر این توکن رو هر چند وقت یکبار رفرش کنیم و تغییر بدیم بدون اینکه کاربر مجدد نام کاربری و کلمه عبور خودش رو ارسال کنه.
    یا کلا نیازی به تغییر این توکن نیست؟ از لحاظ امنیت مشکلی ایجاد نمیکنه؟

    1. سلام.
      خواهش میکنم
      -اگر مقدار exp رو توی payload تعیین نکنیم برای همیشه اعتبارش باقی میمونه

      -برای رفرش و نامعتبر کردن یک توکن یک راه اینه که توکن ها رو در یک جدول داشته باشیم برای ذخیره توکن های که اعتبارشون تمام شده و توکن رو در اون جدول قرار بدیم و قبل از اجازه ورود به کاربر چک کنیم توکن جزو اون لیست نامعتبر نباشه.
      البته میشه از اول زمان اعتبار رو کوتاه در نظر گرفت تا کاربر مجبور به لاگین دوباره بشه و نیاز به رفرش توکن نداشته باشیم.

      -بله اگر تاریخ توکن بگذره یعنی توکن منقضی شده و کاربر دوباره باید مراحل لاگین رو طی کنه.

      -اگر برای احراز هویت و لاگین کاربر از این توکن استفاده می کنید چاره ای جز وارد کردن نام کاربری و رمز عبور نیست.
      از لحاظ امنیتی معمولا توصیه میشه توکن هر چند وقت یکبار تغییر کنه. اما در کل تا وقتی این توکن دست فرد دیگه ای نیافتاده مشکلی وجود نداره.

  13. سلام اقای محمره وقت بخیر
    میخواستم بدونم سایت هایی مثل دیوار و شیپور که بدون احراز هویت میشه آگهی ها سایت دید , چطوری api باز اطلاعات جیسون مخفی میکنن و امکان مشاهده اطلاعات در قسمت inspect بروزر وجود نداره؟

    1. سلام شیپور رو چک نکردم ولی دیورار رو خودم از طریق api اسکرپ کردم. مخفی نیست.
      باید توی تب xhr داخل inspect با دقت دنبال response ها بگردی و دیتای آگهی ها رو پیدا کنی.

      api دیوار

دیدگاه‌ خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *