وقتی سایتهای مختلف را بررسی میکنیم شاهد این هستیم که اکثر آنها درحال استفاده از JSON میباشند که اهمیت JSON و تبادل اطلاعات با آن را متوجه میشویم اما یکی از نقشهای مهم JSON را میتوان در JWT اشاره کرد که خیلی از وبسایتها را یک پله از مابقی وبسایتها جلوتر انداخته است، حال سوال پیش میآید که JWT چیست و چه کاربردی دارد؟
JWT چیست؟
JWT به اختصار رسیده سه کلمهی JSON Web Token است، (Open Standard (RFC 7519 است، به این معنا که هرکسی میتواند از آن استفاده کند، و میتوان بین سرور (Server) و کلاینت (Client) و یا سرور به سرور اطلاعات مورد نظر را با خیال راحت انتقال داد.
JWT برای چه استفاده میشود؟ اطلاعات تایید شده و قابل اعتماد هستند، و از همه مهمتر فشرده بودن یا جمع و جور بودن دادهها است، به این معنا که میتوان JWT را از طریق URLها، درخواستهای POST و HTTP Headerها ارسال کرد، فشرده بودن دادهها باعث میشود، تا سرعت انتقال نیز بالا باشد.
از دیگر ویژگیهای JWT میتوان به خوددار بودن آن اشاره کرد، یعنی اطلاعات کاربر را در خودش دارد و از کوئری (Query) زدنهای بیهوده به پایگاهداده جلوگیری میکند. برای مثال تصور کنید که یک کاربر در روز چند بار به وبسایت شما مراجعه میکند اگر از JWT استفاده نکنید شما باید هر بار یک کوئری به پایگاه داده بزنید تا بررسی کنید آیا این کاربر عضو وبسایت شما هست یا آیا از قبل لاگین بوده است. ولی وقتی از JWT استفاده میکنید فقط کافیست توکن (Token) کاربر را بررسی کنید در ادامه دربارهی توکنها هم توضیح میدهیم.
JWT برای مجوزهای دسترسی (Authorization) استفاده میشود نه برای اعتبارسنجی (Authentication) این دو کاملا معنی و مفهوم متفاوتی دارند، حال باید به توضیحات این دو مورد بپردازیم.
اعتبارسنجی یا Authentication
عمل اعتبارسنجی زمانی استفاده میشود که شما میخواهید دادهای که از سمت سرور میآید را اعتبارسنجی کنید که این پروسه در فرمهای صفحات وب خیلی مورد استفاده قرار میگیرد. برای مثل شما نام کاربری و رمز عبور و ایمیل یک کاربر را موقع ثبت نام دریافت میکنید و آن را بررسی میکنید که آیا از ساختار درستی برخوردار است؟ یا آیا اطلاعات ورودی میتوانند مخرب باشند؟
مجوزهای دسترسی Authorization
Authorization یک پروسه برای دسترسی دادن به انواع مختلف کاربران یا برای دسترسی دادن برای انجام کاری است، برای مثال شما یک وبسایت دارید که درون آن کاربرانی با عنوان مدیر، فروشنده، نویسنده و ... وجود دارد و شما به انواع این کاربران دسترسیهای مختلف میدهید تا کارهای مربوط به خودشان را انجام دهند، به این معنا که دسترسیهای مدیر را نویسنده و یا فروشنده ندارد.
ساختار JSON Web Token چگونه است؟
ساختار JWT به سه بخش تقسیم میشود که با . (dot) از هم جدا میشوند:
Header
Payload
Signature
و ساختاری شبیه به xxxxx.yyyyy.zzzzz
Header چیست؟
Header از دو بخش تشکیل شده است که بخش اول الگوریتم آن را تعیین میکند و بخش دوم نوع آن را مشخص میکند، که طبیعتاََ JWT است.
این ساختار JSON با Base64Url رمز گذاری شده است تا به بخش اول آن شکل بدهد، منظور از بخش اول xxxxx.yyyyy.zzzzz است.
برای مثال:
{
"alg": "HS256",
"typ": "JWT"
}
الگوریتمهای Header میتوانند HMAC SHA256 یا RSA باشند.
الگوریتم HMAC SHA256
HMAC SHA256 الگوریتم هش کلیددار است که از هش SHA-256 ساخته شده است که مبتنی بر کد احراز هویت میباشد، فرایند کار اینگونه است که HMAC یک کلید مخفی را با دادههای پیام مخلوط میکند و نتیجه را با تابع هش، هش میکند، مقدار هش را دوباره با کلید مخفی مخلوط میکند و بار دیگر تابع هش را اعمال میکند و پس از خروجی، هش 256 بیت است.
از HMAC میتوان برای بررسی پیام ارسال شده استفاده کرد، بدین صورت که آیا از طریق کانالی ناامن دستکاری شده است، در صورتیکه فرستنده و گیرنده یک کلید مخفی را به اشتراک میگذارند، فرستنده مقدار هش را برای دادههای اصلی محاسبه میکند و دادهی اصلی و هم مقدار هش را به عنوان یک پیام واحد ارسال میکند. گیرنده از پیام دریافتی مقدار هش را دوباره محاسبه میکند و بررسی میکند HMAC محاسبه شده با HMAC منتقل شده مطابقت دارد یا خیر، و لازم به ذکر است که هرگونه تغییر در هش یا داده باعث عدم تطابق میشود.
الگوریتم RSA
RSA الگوریتم رمزنگاری نامتقارن است. نامتقارن در حقیقت به این معنی است که روی دو کلید مختلف یعنی کلید عمومی و کلید خصوصی کار میکند. همانطور که از نام پیداست کلید عمومی به همه داده میشود و کلید خصوصی در آن خصوصی نگه داشته میشود. برای مثال:
کاربر به یک سرور یک کلید عمومی ارسال میکند و یک سری داده درخواست میکند.
سرور دیتا درخواست شده را با کلید عمومی رمزنگاری میکند و داده رمزنگاری شده را ارسال میکند.
کاربر آن دیتا را دریافت میکند و آن را با کلید خصوصی رمزگشایی میکند.
از آنجایی که این رمزنگاری نامتقارن است کسی غیر از کاربر نمیتواند دادهها را دریافت کند و رمزگشایی کند حتی اگر فرد سومی کلید عمومی را داشته باشد.
Payload چیست؟
بخش دوم xxxxx.yyyyy.zzzzz) Token) را Payload تشکیل میدهد و شامل یک سری اطلاعات دربارهی کاربر و یک سری دادههای اضافیاند، که استفاده از آنها خیلی رایج است. Payloadها میتوانند ساختاری شبیه به زیر داشته باشند.
همانطور که در کدهای بالا آوردهایم یک کلید به اسم sub داریم و یکسری اعداد در مقابل آن است که در اینجا میتوانند به عنوان آیدی (id) کاربر استفاده شود، روبهروی کلید name مقدار نام کاربر قرار میگیرد و در کلید بعدی admin بودن و یا نبودن کاربر را مشخص میشود. پس متوجه شدیم که این بخش شامل اطلاعات کاربرها است.
Signature چیست؟
آخرین و مهمترین بخش JWTها Signature است، در حقیقت Signature تایید میکند که آیا دادهای که از سمت کاربر برگشته است بهم ریخته و یا دستکاری شده است؟
الگوریتمی که ما برای رمزنگاری در Header دادهایم را میگیرد، header و Payload را ترکیب میکند و آنها را دوباره رمزنگاری میکند و بار دیگر با کلیدی که به Signature داده ایم رمزنگاری میکند و اگر کسی مقدار رمزگذاری شده را تغییر دهد خطایی دریافت میکنیم که به ما نشان میدهد Signature معتبر نمیباشد.
و در نهایت خروجی رمزگذاری و تمام اینها با یکدیگر به شکل زیر میباشد.
مقایسه JWT با Sessionها
برای درک بهتر تفاوت بین Session و JWT این دو را با مثال مقایسه میکنیم.
Session
شما به عنوان یک کاربر در یک سایتی لاگین میکنید و اطلاعات وارد شده شما به سمت سرور میرود، ایمیل و رمزعبور شما در Session ذخیره میشود و آیدی Session شما به عنوان Cookie برمیگردد، دفعه بعد که شما وارد آن سایت بشوید حالا آیدی Session شما به سمت سرور ارسال میشود و سرور با آیدی، شما را تایید میکند و پاسخ به شما برمیگردد. پس در Sessionها همه چیز به سرور بستگی دارد.
JWT
در JWT هم شما به عنوان یک کاربر وارد یک سایت میشوید و لاگین میکنید، اطلاعات وارد شده شما به سمت سرور میرود و یک JWT با رمز برای شما ساخته میشود و JWT ساخته شده به سمت مرورگر شما ارسال میشود، دفعه بعد که شما وارد سایت بشوید یک درخواست از مرورگر با JWT به سمت سرور میرود و JWT با Signature تایید میشود و مشخصات کاربر از JWT گرفته میشود سپس پاسخ به سمت مرورگر ارسال میشود.
در JWT همه چیز سوار بر مروگر شما میباشد نه سرور، پس مزیت این کار کجاست؟
کاربرد JSON Web Token
فرض کنید که شما به یکی از وبسایتهای بانکی مراجعه کردهاید و مشخصات خودتان را وارد کردهاید و لاگین شدهاید و وقتی دارید کارهای بانکی خودتان روی یکی از سرورها انجام میدهید، سایت تصمیم میگیرد که شما را به یکیدیگر از سرورها منتقل کند به دلیل اینکه به سمت سرور کنونی درخواستهای زیادی آمده است و بدون اینکه اتفاقی برای اطلاعات شما پیش بیاید شما منتقل میشوید و کارهای خودتان را انجام میدهید.
در اینجا از JSON Web Token استفاده شده است چون اطلاعات شما در مروگرتان با JWT ذخیره شده است و نیازی نیست که دوباره در سرور جدید لاگین کنید ولی اگر از Sessionها استفاده میشد شما مجبور بودید که دوباره اطلاعات خود را وارد کنید تا به انجام ادامهی کار خود بپردازید.
ساخت JSON Web Token در PHP
تا اینجای مقاله ما به درکی از JWT رسیدیم و فهمیدیم که چهگونه کار میکند و در چه جاهایی مورد استفاده قرار میگیرد. حال باید یک نمونه از آن را در PHP بسازیم.
چگونه Header و Payload بسازیم؟
همانطور که میدانید باید Header و Payload را در ساختار JSON دربیاوریم، برای این کار لازم است اول آنها را به صورت آرایه بنویسیم و بعد به ساختار JSON تبدیل کنیم.
در کد بالا ما یک ساختار آرایه آوردهایم و همانطور که در بالا هم اشاره شد دارای دو کلید به نامهای typ که نوع آن را مشخص میکند و alg که الگوریتم را مشخص میکند، حال آرایه را به JSON با تابع json_encode تبدیل کردهایم و آن را در متغیری با نام header ذخیره کردهایم.
در کد بالا نیز ما مقادیر Payload خود را در ساختار آرایه آوردیم و آن را به JSON با تابع ()json_encode تبدیل کردهایم و در متغیری با نام Payload نگه داشتهایم.
ساخت رشته Base64Url برای Header و Payload
در این قدم ما Header و Payload را به رشته Base64Url رمزنگاری خواهیم کرد اما در PHP ما تابع مخصوصی برای این کار نداریم. یک تابع به اسم ()base64 وجود دارد که با base64Url یک سری تفاوتهایی دارد و ما مجبوریم که تغییراتی ایجاد کنیم، برای مثال + ، / و = را با - ، _ و رشته خالی (" ") جایگذاری میکنیم.
در کد بالا جایگذاریها انجام شده و متغیر header با تابع ()base64_encode رمزنگاری شده است و در متغیر base64UrlHeader ریخته شده است، این متغیر شامل مقدار زیر است.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
و این کار را برای متغیر Payload نیز تکرار میکنیم.
به مهمترین بخش رسیدیم که باید Signature را بسازیم، برای ساخت SIgnature باید از تابع ()hash_hmac و از الگوریتم sha256 استفاده کنیم، لازم به ذکر است که مقادیر درون متغیرهای base64UrlHeader و base64UrlPyload با یک . (dot) از هم دیگر جدا میشوند.
پارامتر اول: تابع ()hash_hmac ما باید نوع الگوریتم هش را تعیین کنیم.
پارامتر دوم: دادهای که قرار است هش بشود را وارد میکنید.
پارامتر سوم: یک کلید مخفی برای تولید hmac است.
پارامتر چهارم: وقتی که مقدار true را میدهیم ، دادههای باینری را به عنوان خروجی به ما برمیگرداند و false هگزیتهای (hexit) حروف کوچک را به عنوان خروجی برمیگرداند.
رمزنگاری Signature تولید شده
حال که Signature خود را تولید کردهایم و آن را درون متغیر signature ذخیره کردهایم باید آن را مانند header و Payload به صورت base64Url رمزنگاری کنیم.
تمامی مقادیری که ما رمزنگاری کردیم و درون متغیرها نگهداری کردیم باید به یکدیگر بچسبانیم تا مانند ساختار JWT بشود، برای این کار باید متغیرها را با . (dot) از هم جدا کنیم و درون متغیری با نام jwt بریزیم.
تا الان فهمیدیم که JWT چیست و چه کاربردی دارد و متوجه شدیم که چقدر میتواند هم به ما و هم به کاربر کمک بسزایی داشته باشد و فهمیدیم که JWT چقدر به اطلاعات کاربر حساس است و بسیار اهمیت میدهد، از مزیتهای JWT این است که محدود به هیچ زبان برنامهنویسی نیست و شما با هر زبان برنامهنویسی که کار میکنید میتوانید آن را به کار بگیرید و از آن استفاده کنید پس برای فراگیری آن وقت بگذارید.
۲۰ دیدگاه
۲۵ دی ۱۴۰۱، ۲۳:۵۶
با سلام و وقت بخیر ,
من 2 تا سایت دارم و میخوام کاربر از سایت اول با کلیک روی لینک دانلود به سایت دوم منتقل بشه و در صورتی که از سایت اول وارد شده باشه یک پیام مشاهده کنه و در صورتی که به صورت مستقیم و یا از دیگر سایتها وارد سایت دوم بشه این پیام ظاهر نشه.
چطور میتونم این کارو انجام بدم ؟
نمیخوام این کارو با HTTP referer انجام بدم .
مثلا لینک دانلود در سایت اول هر بار که صفحه رفرش میشه یه کد api یا key داشته باشه و با کلیک و انتقال به سایت دوم این کدها کاربر رو به مرحله بعد برسونه.
ممنون میشم راهنماییم کنید.
نازنین کریمی مقدم۲۶ دی ۱۴۰۱، ۱۰:۲۸
درود
تا جایی که اطلاع دارم http referrer برای اینکار ساخته شده و <a href="https://stackoverflow.com/questions/6147309/get-referrer-page-without-http-referer" target="_blank" rel="noopener nofollow ugc">راه خاصی وجود نداره</a> مگر اینکه در همون صفحه سایت اول پارامترها رو ارسال کنید (بعدش دیگه چون نمیفهمه از کجا اومده نمیتونه پیام رو ست کنه)
باز پیامتون رو تایید کردم اگر کسی ایده ای داشت کمک تون کنه.
ali۱۷ آبان ۱۴۰۲، ۰۵:۴۵
header که استفاده کردی اخر ؟ بزار و به صورت get یک متغیر ارسال کن به صفحه بعد بقیه کار ولی استاندارد نیس زیاد
ایمان۰۸ آذر ۱۴۰۲، ۱۲:۵۴
وب سایت اول اگر توکن یکبار مصرف امضا شده در لحظه مشاهده لینک دانلود در صفحه اول بسازد و سایت دوم با بررسی امضا از معتبر بودنش مطمئن شود منطقا میتونی بدون هیچ بررسی دیگری اطمینان حاصل کنید که لینک را سایت اول داده و چون یکبار مصرف هم هست نگرانی دیگری نداری برای جعل و کپی و ...
[1] https://medium.com/@swayamraina/symmetric-vs-asymmetric-jwts-bd5d1a9567f6
[2] https://curity.io/resources/learn/jwt-best-practices/
۰۶ آبان ۱۴۰۱، ۰۹:۱۴
سلام, یه سوال داشتم...
حالا که ما محتوامون رو انکریپت کردیم چجوری دیکریپت کنیم؟ کدشو از کجا میتونم ببینم؟ مرسی
نازنین کریمی مقدم۱۵ آبان ۱۴۰۱، ۰۶:۲۳
درود
اولین کامنت همین صفحه رو بخونید که آقای صالحی توضیح دادند.
۰۱ اسفند ۱۴۰۰، ۱۸:۳۰
سلام وقتی توکن رو از کاربر گرفتین معتبر بودن رو چطور بررسی کنیم
نازنین کریمی مقدم۰۳ اسفند ۱۴۰۰، ۱۲:۲۷
درود
این موضوع در تقویم محتوایی ما هست و بعد از ویرایش مقالات قدیمی سراغش میریم.
اما در کل چندتا کامنت پایینتر آقای صالحی درمورد خواندن توکن و تاییدش پاسخ دادند حتما بررسی کنید.
سیدرضا بازیار۰۸ دی ۱۴۰۰، ۱۷:۲۲
با تشکر از امیر صالحی عزیز بابت این مقاله مفید
این مقاله + سرفصل jwt در دوره آموزش متخصص php سون لرن (با تدریس دکتر لقمان آوند) باعث میشه که این مبحث رو خیلی خوب یاد بگیریم
مسعود هارونی۱۳ آذر ۱۴۰۰، ۰۸:۵۷
ممنونم از مقاله ی خوبتون.
فرزاد۰۸ شهریور ۱۴۰۰، ۰۵:۰۵
باتشکر از مطالب مفیدتون
فقط یک نکته ای رو بگم خدمتتون
بیشتر سایتها تا همین مرحله بیشتر توضیح نمیدن
در مورد محل ذخیره سازی jwt در سمت سرور ، پاک کردن و ... هم توضیح بدید.
نازنین کریمی مقدم۱۳ شهریور ۱۴۰۰، ۲۰:۳۰
درود
حتما درخواستتون رو بررسی میکنیم و در صورت امکان یه مقاله راجبشون منتشر میکنیم.
ممنون که با ما همراه هستید.
ستاره۱۹ مرداد ۱۴۰۰، ۱۰:۴۱
سلام
کد jwt دارم که یک وب سرویس هستش
میخوام داخل وردپرسم بگذارم چطور این کار انجام بدم
نازنین کریمی مقدم۲۴ مرداد ۱۴۰۰، ۱۱:۰۴
درود
حقیقتا یه مقاله آموزشی در تقویم مون قرار دادیم تا ببینیم کی نویسندگان این بخش بهش میرسند.
برای اضافه کردنش باید یا دستی به سورس وردپرس اضافه کنید و یا از پکیج هایی مثل jwt-authentication-for-wp-rest-api استفاده کنید. توصیه میکنم عبارت jwt رو در بخش افزونهها سرچ کنید و امکاناتشون رو مطالعه کنید.
سایر دوستان اگر تجربه ای در این زمینه دارید به این دوستمون کمک برسونید :)
Danial Hajirajabi۱۴ خرداد ۱۴۰۰، ۱۹:۵۰
به عنوان یه مفاله فارسی عالیه و لی ای کاش مثال واقعی و عملی رو هم میزدید در یک مقاله دیگ حداقل.??
نازنین کریمی مقدم۱۶ خرداد ۱۴۰۰، ۱۶:۵۲
درود
درخواستتون رو بررسی میکنیم و در صورت امکان حتما یه مقاله دیگه از این موضوع رو پوشش میدیم.
ممنون که با ما همراه هستید.
M۲۳ اسفند ۱۳۹۹، ۱۱:۲۱
خیلی عالی
فقط چطور بدون نصب پکیج دیکود کنیم؟
از سایت یا چیزی دیگه ای میشه استفاده کرد؟
[code]
Your Code Here
[/code]
[code]
Your Code Here
[/code]
نازنین کریمی مقدم۲۶ اسفند ۱۳۹۹، ۱۰:۱۵
درود.
من شخصا تست نکردم اما <a href="https://jwt.io/" target="_blank" rel="noopener nofollow ugc">سایت jwt.io </a>هست :)
محمد جواد۱۱ اردیبهشت ۱۳۹۹، ۱۷:۰۹
سلام . عالي بود
فرض كنيد با استفاده از روش گفته شده يك jwt token ايجاد كرديم . حالا چطور ميتونيم اين توكن رو ديكد يا تاييد كنيم ؟
امیر صالحی۱۵ اردیبهشت ۱۳۹۹، ۰۹:۰۱
سلام، ممنون
توی <a href="https://packagist.org/" rel="nofollow ugc">packagist</a> پکیجهای وجود داره که میتونید به راحتی JWT خودتونو کنترل کنید، برای نصب این پکیجها شما نیاز به Composer دارید.
بعد از نصب این مورد میتونید پیکجهای زیر نصب کنید و از مستنداتشون برای کنترل کردشون استفاده کنید.
<a href="https://packagist.org/packages/okta/jwt-verifier" rel="nofollow ugc">jwt-verifier</a>
بعد از نصب و خواندن پکیج بالا پیشنهاد میشه پکیج زیر رو هم نصب کنید. پکیج بالا به یک کتابخانه سازگار با PSR-7 نیاز دارد که با نصب پکیج زیر میتوان مورد استفاده قرار داد.
composer require spomky-labs/jose guzzlehttp/psr7
شروع رایگان یادگیری برنامه نویسی
کلیک کنید 👇
دوره الفبای برنامه نویسی با هدف انتخاب زبان برنامه نویسی مناسب برای شما و پاسخگویی به سوالات متداول در شروع یادگیری موقتا رایگان شد: