JWT چیست آموزش استفاده از آن + ویدیویی ( بروزرسانی دوم )

JWT چیست آموزش استفاده از آن + ویدیویی ( بروزرسانی دوم )

JWT چیست آموزش استفاده از آن – کلمه jwt به معنای json web token می باشد که در هر پلتفرمی قابل استفاده است که برای Authorization تایید دسترسی کاربر به منابع مورد استفاده قرار می گیرد .

jwt از پروتکل oauth 2 بهره می برد که یک پروتکل استاندارد محسوب می شود ، یکی از نمونه های بارز آن توییتر است که از این نوع اعتبار سنجی استفاده می کند .

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

اما JWT یک راه بهتر و با امنیت بالا می باشد که در عین حال بسیار انعطاف پذیر تر می باشد و در پلتفرم های دیگر مثل android و ios مورد استفاده قرار می گیرد .


آموزش ویدیویی JWT ( مطالب مقاله )

لینک یوتیوب آموزش .

JWT چگونه کار می کند ؟

JWT چیست آموزش استفاده از آن

بخش ساخت توکن

همانطور که در تصویر بالا مشاهده می کنید از 3 بخش تشکیل می شود :

1- Header : این بخش مشخص کننده الگوریتم رمزگذاری است و اطلاعاتی در مورد آن می دهد
2- Payload : داده هایی که می خواهیم ذخیره کنیم و بعدا از آن استفاده کنیم ، مثل نام کاربری و ایمیل
3- Signature : این بخش از ادغام بخش 1 و 2 به صورت رمزگذاری base64 است و secret key است .
در واقع secret key باید یک رمز پیچیده باشد تا باعث بالا رفتن امنیت رمزنگاری شود .

این خیلی مهم است که secret key در اختیار افراد غیر قابل اعتماد قرار نگیرد در صورتی که قرار گیرد احتمال پیدا کردن توکن بدون نیاز به دسترسی وبسایت بالا رفته و امنیت پایین می آید .

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

eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6IjEyMzQ1Njc4OTAiLCJ1c2VybmFtZSI6ImFybWFuMDEifQ.qsLOFsFLyM__b6biNKkwMBJpNxhnQuD23vDyKMBeXvbMNx9otLrbHPySp8Nsvnpu

این توکن به این شکل رمزگذاری شده :

Header . Payload . Signature


تایید توکن ساخت شده JWT

پس از ساخت توکن در کوکی ذخیره می شود و هر وقت که کاربر بخواهد به منبعی مثل پروفایل خود برود توکن آن به سرور فرستاده می شود تا مورد بررسی قرار گیرد .

اکنون بخش اول توکن بالا یعنی header را بخواهیم با تابع base64_decode به رشته معمولی برگردانیم به این شکل می شود :

echo base64_decode("eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9"); // {"alg":"HS384","typ":"JWT"}


همچنین بخش 2 یعنی payload :

echo base64_decode("eyJlbWFpbCI6IjEyMzQ1Njc4OTAiLCJ1c2VybmFtZSI6ImFybWFuMDEifQ");// {"email":"1234567890","username":"arman01"}


پس سرور می آید توکن فرستاده شده کاربر را به طور موقت در متغیر ذخیره می کند تا بررسی کند که آیا معتبر هست یا خیر . بنابراین با 2 تکه ای که بدست آمده و secret key رمزگذاری می کند تا ببیند که آیا این توکنی که کاربر فرستاده با توکن خود همخوانی دارد یا خیر .

در صورتی که یکی باشد امکان دسترسی به کاربر داده می شود در غیر اینصورت پیغام :

Signature verification failed

می دهد .

ساخت و تایید jwt به صورت عملی

ساخت jwt

برای شروع کار ابتدا کتابخانه مخصوص PHP را با composer به پروژه خود اضافه نمایید :

composer require firebase/php-jwt

اکنون کد زیر را اضافه کنید ( توضیحات در پایین کد ) :

require_once "vendor/autoload.php";
 
use \Firebase\JWT\JWT;
use Firebase\JWT\Key;
 
$secret_key = "ELITE_CODERS_SECRET_KEY";
 
$day = 1;
$current_time_base_second = time();
$nbf = $current_time_base_second + 0;
$exp = $current_time_base_second + 86400 * $day;
 
$payload = array(
    "iss" => "https://rapidcode.ir",
    "aud" => "http://rapidcode.io",
    "iat" => $current_time_base_second, 
    "nbf" => $nbf, 
    "exp" => $exp,
    "data" => array( 
        "email" => "info@rapidcode.ir",
        "exp_time" => $exp,
        "readable_time" => date("Y/m/d H:i:s" , $exp)
    )
);
 
try{
    $jwt = JWT::encode($payload , $secret_key , "HS384");
    echo ($jwt . "\n\n");

    $data = JWT::decode($jwt , new Key($secret_key , "HS384"));
    var_dump($data);
 
}catch(Exception $e){
    echo $e->getMessage();
}

لاین 6 : رمز مخفی یا secret key تاکید می شود که نباید در اختیار هر کسی قرار گیرد . یک رمز پیچیده برای این بخش در نظر بگیرید .

لاین 14 : این که توکن را چه وبسایتی ساخته و تایید می کند
لاین 15 : در صورتی که چندین دامنه مرتبط با یکدیگر داشته باشیم و بخواهیم برای دامنه rapidcode.io از دامنه rapidcode.ir استفاده کنیم
لاین 16 : زمان ساخت توکن بر اساس timestamp
لاین 17 : اینکه پس از ساخت تا چه مدت بعد توکن قابل استفاده نباشد .
لاین 18 : توکن تا چه زمانی معتبر باشد از آنجایی که در لاین 11 برای 1 روز تنظیم کردیم .
لاین 19 : داده هایی که متناسب با پروژه مان استفاده می کنیم مثلا ایمیل یا نام کاربری ، همچنین چند داده اضافی هم می توانیم داشته باشیم که اختیاری است .


لاین 27 : با تابع encode که متعلق به کلاس JWT است توکن را می سازیم این تابع دارای 3 ورودی است :

1- payload : داده هایی که مرتبط با کاربر است و بعدا مورد استفاده مان قرار می گیرید
2- secret key : رمز مخفی برای جلوگیری از پیداکردن الگوریتم ساخت توکن
3- الگوریتم رمزنگاری : پیشفرض HS256 است اما می تواند فرق داشته باشد ( اختیاری )

تایید و رمزگشایی توکن JWT

تابع JWT::decode دو کاربرد داره که هم تایید می کند که توکن معتبر است و هم داده را از توکن بیرون می کشد و به ما برمی گرداند .
نمونه آن در کد بالا می باشد .

این تابع دارای 2 ورودی ( اجباری ) است .

در بروزرسانی جدید با کمک کلاس Key ورودی دوم را می دهیم . ( 15 خرداد 1401 )

1- token : همان توکنی که کاربر در هر بار استفاده از منابع به سرور می فرستد
2- key : همان secret key + الگوریتم رمزنگاری در صورتی که ورودی دوم JWT::encode را وارد نکردید HS256 بدهید در صورتی که الگوریتم دیگری مثل HS384 داده اید همان را وارد کنید .

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

تست و خطای jwt

برای اینکه مطمئن شویم برنامه به درستی کار می کند به صورت دستی تغییری در توکن بدهید مثلا چند کاراکتر به توکن اضافه کنید .

حالا اگر خطایی دریافت کردید نشان دهنده این است که سرور متوجه شده که این توکن نامعتبر است و سرور نقشی در ساخت آن نداشته . خطای زیر نشان دهنده ی همین است :

Signature verification failed

ارسال نظر

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

لیست نظرات

  1. علی
    علی

    اطلاعات تون خوبه اما این ویدیو اصلا ویدیوی خوبی نیست

    20 اسفند 1401 | 11:18:56
  • حسین باقری
    حسین باقری

    سعی شده که مطلب به ساده ترین شکل ممکن به هنرجو انتقال داده بشه . ( البته هنرجو هم یکم باید آی کیو داشته باشه 😉 )

    20 اسفند 1401 | 16:58:20
  • محمود رنجبر نورآبادی
    محمود رنجبر نورآبادی

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

    15 آبان 1401 | 11:02:49
    • حسین باقری
      حسین باقری

      سوال 1 : خیر مشکلی ایجاد نمی کنه . سوال 2 : تا زمانی که شما پکیج رو بروزرسانی نکنید کد به همون شکل قبلی کار میکنه اما توصیه میشه که همیشه با نسخه بروز همراه باشید چرا که ویژگی های و باگ های احتمالی رفع می شه .

      15 آبان 1401 | 13:51:16
  • محمود رنجبر نورآبادی
    محمود رنجبر نورآبادی

    سلام حسین جان احسنت به دانش فنی بالا یه سوال دیگه من علاوه بر ایمیل آیدی کاربر رو هم در اطلاعات قرار دادم به لحاظ امنیتی که مشکلی ایجاد نمیکنه درسته ؟

    15 آبان 1401 | 10:37:01
  • محمود رنجبر نورآبادی
    محمود رنجبر نورآبادی

    مهندس باقری وقتی کدها رو بصورت کلاس استفاده میکنم برای متغییر private $current_time_base_second = time(); خطای زیر رو دریافت میکنم چطوری اونو رفع کنم ؟ constant expression contains invalid operations

    14 آبان 1401 | 17:04:06
    • حسین باقری
      حسین باقری

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

      private $current_time_base_second; 
      function __construct() {
       $this->current_time_base_second = time(); 
      }
      
      14 آبان 1401 | 20:06:47
  • محمود رنجبر نورآبادی
    محمود رنجبر نورآبادی

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

    14 آبان 1401 | 16:58:03
    • حسین باقری
      حسین باقری

      باید جدول مخصوص توکن ورود کاربران داشته باشید و اگر توکن جدیدی ساخته شد column منقضی رو true قرار بگیره .

      14 آبان 1401 | 20:00:59
  • محمود رنجبر نورآبادی
    محمود رنجبر نورآبادی

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

    14 آبان 1401 | 16:53:15
    • حسین باقری
      حسین باقری

      درود ، برای این موضوع باید کلا دسترسی به مرورگر به هر کسی داده نشه چرا که به راحتی می تونه وارد پسورد های مرورگر بشه و به جای یک سایت اطلاعات ورود تمامی سایت ها رو به سرقت ببره پس موضوع بر میگرده به عدم اعتماد شخص به هر کس . اما برای جلوگیری این موضوع بهتر هست که مدت زمان اجازه " مرا به خاطر بسپار " روی مدت کوتاه باشه تا کاربر مجبور به لاگین مجدد بشه . روش دومی که میشه استفاده کرد هم اینه که آیپی کاربر رو برای زمانی که به سیستم لاگین میشه رو ذخیره کنیم و اگر همون کاربر با آیپی دیگر وارد شد توکن رو منقضی کرده و مجبور به لاگین مجدد کنیم .

      14 آبان 1401 | 19:58:58
  • محمود رنجبر نورآبادی
    محمود رنجبر نورآبادی

    سلام مهندس باقری عزیز خداقوت برای بخش لاگین کاربران و خروج اونها از حساب کاربری من با سشن همه چیز رو پیاده کردم ولی با jwt نمیدونم دقیقا چطور پیاده سازی کنم یه توضیح کامل برای من ارائه بفرمایید بی زحمت سوال یک مثلا وقتی که کاربر ثبت نام کرد توکن رو بسازم یا وقتی برای اولین بار میخواد لاگین کنه ؟ سوال دو وقتی توکن رو در کوکی ذخیره کردیم عمر کوکی چقدر باشه عمر توکن چقدر باشه ؟ سوال سه وقتی یوزر نیم و پسورد درست وارد شد ما اول باید چک کنیم که مثلا کوکی توکن ست شده یا نه بعد مقدار اون رو اعتبار سنجی کنیم درسته؟ سوال ۴ حالا وقتی اعتبار سنجی درست بود چیکار کنیم مثلا میخوام کاربر به چندین صفحه دسترسی داشته باشه وقتی سشن بود میگفتیم اگر فلان سشن ست شده اجازه ورود بده اینچا چی بنویسیم شرط چی باشه ؟ ممنون میشن ذهنمون رو فعال کنید و به سوالات پاسخ بدید اصلا یه دوره تصویری براش بزارید

    10 تیر 1401 | 19:44:58
    • حسین باقری
      حسین باقری

      پاسخ سوال 1 و 2: معمولا موقعی که کاربر لاگین میشه حالا اگر تیک مرا به خاطر بسپار زد به جای استفاده از session از کوکی بایک مدت اعتبار ذخیره میشه که معمولا روی 90 روز هست .
      پاسخ سوال 3 : بعد از این که نام کاربری و رمز عبور به درستی وارد شد در صورتی که توکنی از قبل بود حذف بشه و توکن جدید روی کوکی ثبت بشه .
      پاسخ سوال 4 : می تونید لیست صفحات روی توی توکن اضافه کنید مثلا صفحات لاگین و ثبت نام اگر وارد شد کاربر رو ریدایرکت کنه به داشبورد چون از قبل ثبت وارد شده و نیازی نیست اطلاعات وارد کنه این اطلاعات رو راحت می تونید روی توکن اضافه کنید .

      10 تیر 1401 | 23:31:21
  • Dude
    Dude

    سلام. واسه دیکود کردن توکن, شما به صورت دستی توکن وارد کردید ولی واسه دریافت توکن و دیکود کردن از چه روشی استفاده میشه کرد؟ من از روش پایین استفاده میکنم ولی خطا میده؟

    require_once "vendor/autoload.php";
     
    use \Firebase\JWT\JWT;
    
     $data = json_decode(file_get_contents("php://input"));
    
    $secret_key = "ELITE_CODERS_SECRET_KEY";
    
    $jwt=isset($data ->jwt) ? $data->jwt : "";
    
    if($jwt){
     
        try {
     
            $decoded = JWT::decode($jwt, $secret_key, array('HS384'));
    
            echo json_encode(array(
                "message" => "Access granted: ".$jwt,
                "error" => $e->getMessage()
            ));
     
        }catch (Exception $e){
     
        http_response_code(401);
     
        echo json_encode(array(
            "message" => "Access denied.",
            "error" => $e->getMessage()
        ));
    }
     }
    
    ارور زیر ===> Wrong number of segments
    14 خرداد 1401 | 22:16:13
    • حسین باقری
      حسین باقری

      درود ، خیلی ممنونم بابت نظر تون این کتابخانه بروزرسانی شده و دلیل خطا تون هم اینه که برای متود decode ورودی دوم باید ادغام secret key و نوع رمزنگاری در کلاس Key هست . مقاله رو بروز کردم . اما پیشنهاد می شه قبل از استفاده از php://input که ورودی های raw یا همون json رو هم می تونه بگیره به صورت دستی توکن رو بسازید و به متود decode بدید بعد از این که مطمئن شدید به درستی کار می کنه از php://input استفاده کنید .

      15 خرداد 1401 | 14:25:48
      • Dude
        Dude

        ممنون بابت راهنمایی. بله تغییرات انجام دادم و توکن هم به صورت دستی وارد کردم و توکن تایید شد. ولی زمانی که توکن به صورت دستی وارد نمی شه خطا میده. نوع خطا ==> Wrong number of segments

        15 خرداد 1401 | 19:48:29
        • حسین باقری
          حسین باقری

          احتمالا داده ای که برای decode از php://input وارد می کنید با داده دستی فرق می کنه کد زیر رو در پروژه تون اضافه کنید تا تمامی اطلاعاتی رو لاگ کنید و مشخص بشه که داده وارد شده چه ساختاری داره

          file_put_contents(__DIR__ . "/logger.txt" , var_export(file_get_contents("php://input") , true))
          
          15 خرداد 1401 | 20:46:51
          • Dude
            Dude

            تنها داده ای که دریافت میشه عدد 2 یا ' ' هستش. نمیدونم دلیلش چیه؟؟ require_once 'vendor\autoload.php'; use Firebase\JWT\JWT; use Firebase\JWT\Key; $data =file_put_contents(__DIR__ . "/logger.txt" , var_export(file_get_contents("php://input") ,true)); echo $data; $jwt=isset($data ->jwt) ? $data->jwt : ""; try{ $secret_key = "ELITE_CODERS_SECRET_KEY"; $decoded = JWT::decode($jwt , new Key($secret_key , "HS384")); echo json_encode($decoded); }catch(Exception $e){ echo $e->getMessage(); }

            15 خرداد 1401 | 21:59:17
          • حسین باقری
            حسین باقری

            اگر داده ای که دارید ارسال می کنید از فرم داره ارسال میشه با استفاده از POST_$ و GET_$ قابل دریافت البته با توجه به attribute به نام method باید استفاده بشه اما اگر داده ای که فرستاده می شه به صورت json هست همون php://input کافیه .

            16 خرداد 1401 | 12:26:30
  • محمود رنجبر نورآبادی
    محمود رنجبر نورآبادی

    سلام مهندس باقری یه مثال از لاگین کاربران با jwt می تونید بفرستید به ایمیل من سپاس از راهنمایی تون موفق باشید

    01 اسفند 1400 | 10:23:07
    • حسین باقری
      حسین باقری

      دورد ، بله کد زیر به کار میاد همچنین آموزش ساخت پنل کاربری در php هم کاربردی هست برای این موضوع .

      require_once "vendor/autoload.php";
       
      use \Firebase\JWT\JWT;
       
      $secret_key = "ELITE_CODERS_SECRET_KEY";
       
      $day = 1;
      $current_time_base_second = time();
      $nbf = $current_time_base_second + 5;
      $exp = $current_time_base_second + 86400 * $day;
       
      setcookie("user_token" , "arman.15478e9x6g" , $exp);
      $payload = array(
          "iss" => "https://rapidcode.ir",
          "aud" => "http://rapidcode.io",
          "iat" => $current_time_base_second, 
          "nbf" => $nbf, 
          "exp" => $exp,
          "data" => array( 
      		"user_id" => "x",
      		"username" => "x",
              "email" => "info@rapidcode.ir",
              "exp_time" => $exp,
              "readable_time" => date("Y/m/d H:i:s" , $exp)
          )
      );
      
      try{
          $user_jwt = JWT::encode($payload , $secret_key , "HS384");
       
          echo $user_jwt; // توکن اعتبار سنجی کاربر
       
      }catch(Exception $e){
          echo $e->getMessage();
      }
      
      05 اسفند 1400 | 12:39:27
    contact us