در مورد استاندارد تولید توکن jwt مقاله های خوبی به زبان هم فارسی هم انگلیسی موجود هست نمونه های وطنیش رو از اینجا می تونید مطالعه کنید .
تو اکثر قریب به اتفاق مطالب از روش تولید امضا و نیز بررسی اصالت امضا به روش الگوریتم کلید متقارن (AES ) استفاده می شود.
به این معنی که برای تولید امضا شما یک کلید مخفی دست تون دارید که باز ازهمون کلید استفاده می کنید برای باز تولید امضا .
این روش برای مواقعی خوب هست که پروژه شما کوچیک باشه یعنی تولید کننده توکن هم خودتونید بررسی و تایید کننده توکن هم خودتون .
اما وقتی پروژه بزرگ تر میشه یا شامل مجموعه زیادی از اپلیکیشن ها میشه که همه شون هم نیاز به اعتبار سنجی و هویت سنجی کاربران دارند نیاز هست یک سروری ( منظور بیشتر یک میکرو سرویس هست ) رو برای تولید توکن ها کنار بگذارید که کارش فقط تولید توکن برای کاربران اپلیکیشن های شما ست . اما وظیفه بررسی اصالت اون توکن ها رو می خواهید بسپارید به بخش دیگر مجموعه تون .
خلاصه کلام و به زبان ساده تولید کننده توکن با چک کننده اصالت توکن یکی نیستند، این جور مواقع میروند سراغ تولید توکن به کمک کلید های نامتقارن .
اما روش کار کمی با اون چیزی که تو اغلب مقاله ها می خونید فرق داره .
به این صورت که سرور A دو تا کلید تولید می کنه ( کلید خصوصی و کلید عمومی ) . کلید خصوصی رو نزد خودش نگه میداره و کلید عمومی رو میده به سرور های چک کننده امضا .
روش کار به این صورت ست :
1- ابتدا بخش header و payload اون توکن jwt رو برداشته و با یک تابع hash ی هَش می کند .
hashObj=Hash(header.payload);
2- بعد به کمک کلید خصوصی خودش این مقدار هَش شده را رمز گذاری می کند.
signature=encrypt(hashObj,privateKey)
3- بعد مقدار تولید شده در مرحله دوم رو میزاره تو قسمت امضای توکن jwt
jwt_token=header.payload.signature
بعد این jwt_token رو میده دست کلاینت ( کاربری که میخواد وارد یک سرورB بشود ) .
کاربر به ازای هر درخواستی که به سرور B ارسال می کند این توکن رو هم ضمیمه درخواستش می کنه.
در سمت سرور B وقتی این توکن دستش میرسه مراحل زیر رو طی می کنه تا بتونه اصالت کاربر و امضاش رو تایید / رد کنه .
سرور A قبلا به یک طریقی کلید عمومی رو به دست سرور B رسونده ( مثل کاری که گوگل برای استفاده از سرویس هاش توصیه می کنه کاربران انجام بدن یعنی میری تو سایت گوگل مشخصات سرور خودت رو میدی و ازش یک کلید میگیری این کلید رو برمیداری تو یه جای امنی از پروژه سمت سرورت نگه میداری ) .
1- سرور B ابتدا میاد به کمک کلید عمومی قسمت امضای توکن jwt رو برداشته و رمزگشایی می کنه :
hashObj=decypt(signature,publicKey)
با این کار به همون آبجکت هَش شده میرسه ( خروجی مرحله 1 در سمت سرورA )
2- بعد قسمت header و payload توکن jwt که دستش رسیده برداشته و تابع هََش اونو تولید می کنه :
tempHashObj=Hash(header.payload)
3- در نهایت مقدار تولید شده در مرحله اول و دومش رو باهم مقایسه می کند اگر برابر بود یعنی اصالت توکن دریافتی تایید شد وگرنه پیام خطا به کاربر ارسال می کند ( مثلا استاتوس کد 401)
if( tempHashObj==hashObj){
continue
} else {
error //redirect user to error page
}
به این روش تایید اصالت یک چیزی ( مثلا امضا ) اصطلاحا روشهای سوال و جواب می گویند . به این صورت که هر دو طرف روی راه حلی برای تولید جواب توافق می کنند
مثلا فرض کنید دوستتون میگه 3 و 5 رو بگیر روش یه عملیاتی انجام بده و خروجیش رو برای من ارسال کن.
فرض کنید خروجی درست از نظر دوستتون عدد 666 است . شما و دوستتون فقط نحوه حل مساله رو میدونید اگه نفر سومی این دو عدد 5و 3 رو بگیره اصلا معنی شو نمیدونه . توی مثال بالایی اگه سرور مخرب و فضول C حتی اگه توکن دستش بیفته نمیدونه باهاش چیکار کنه (چرا میتونه خودش رو جای کاربر جابزنه و کارهای خیلی بدی انجام بده همون میشه حمله csrf ).
یادمون نره هدف از تولید توکن jwt این هست که کسی نتونه هویت فرد دیگه رو جعل کنه حتی اگه توکن دستش بیفته . ( برای جلوگیری از حمله csrf هم معمولا یه توکن دیگه ای اضافه می کنن بنام csrf_token که موضوع این مقاله نیست ) .