base_tutorial/7-PHP/JWT_and_Php.md
2025-04-09 10:51:05 +03:30

19 KiB
Raw Blame History

بسم الله الرحمن الرحیم

آموزش JWT در PHP

آموزش JWT در PHP مقدمه

وقتی سایت‌های مختلف را بررسی می‌کنیم شاهد این هستیم که اکثر آن‌ها درحال استفاده از JSON می‌باشند که اهمیت JSON و تبادل اطلاعات با آن را متوجه می‌شویم اما یکی از نقش‌های مهم JSON را می‌توان در JWT اشاره کرد که خیلی از وب‌سایت‌ها را یک پله از مابقی وب‌سایت‌ها جلوتر انداخته است، حال سوال پیش می‌آید که JWT چیست و چه کاربردی دارد؟ PHP - JWT - JSON

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 باشند. PHP - JWT - header - Pyalod - Signature الگوریتم HMAC SHA256

HMAC SHA256 الگوریتم هش کلیددار است که از هش SHA-256 ساخته شده است که مبتنی بر کد احراز هویت می‌باشد، فرایند کار این‌گونه است که HMAC یک کلید مخفی را با داده‌های پیام مخلوط می‌کند و نتیجه را با تابع هش، هش می‌کند، مقدار هش را دوباره با کلید مخفی مخلوط می‌کند و بار دیگر تابع هش را اعمال می‌کند و پس از خروجی، هش 256 بیت است.

از HMAC می‌توان برای بررسی پیام ارسال شده استفاده کرد، بدین صورت که آیا از طریق کانالی ناامن دستکاری شده است، در صورتیکه فرستنده و گیرنده یک کلید مخفی را به اشتراک می‌گذارند، فرستنده مقدار هش را برای داده‌های اصلی محاسبه می‌کند و داده‌ی اصلی و هم مقدار هش را به عنوان یک پیام واحد ارسال می‌کند. گیرنده از پیام دریافتی مقدار هش را دوباره محاسبه می‌کند و بررسی می‌کند HMAC محاسبه شده با HMAC منتقل شده مطابقت دارد یا خیر، و لازم به ذکر است که هرگونه تغییر در هش یا داده باعث عدم تطابق می‌شود.

الگوریتم RSA

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

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

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

Payload چیست؟

بخش دوم xxxxx.yyyyy.zzzzz) Token) را Payload تشکیل می‌دهد و شامل یک سری اطلاعات درباره‌ی کاربر و یک سری داده‌های اضافی‌اند، که استفاده از آن‌ها خیلی رایج است. Payloadها می‌توانند ساختاری شبیه به زیر داشته باشند.


{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

همان‌طور که در کدهای بالا آورده‌ایم یک کلید به اسم sub داریم و یک‌سری اعداد در مقابل آن است که در اینجا می‌توانند به عنوان آیدی (id) کاربر استفاده شود، روبه‌روی کلید name مقدار نام کاربر قرار می‌گیرد و در کلید بعدی admin بودن و یا نبودن کاربر را مشخص می‌شود. پس متوجه شدیم که این بخش شامل اطلاعات کاربرها است. ## Signature چیست؟

آخرین و مهم‌ترین بخش JWTها Signature است، در حقیقت Signature تایید می‌کند که آیا داده‌ای که از سمت کاربر برگشته است بهم ریخته و یا دستکاری شده است؟

الگوریتمی که ما برای رمزنگاری در Header داده‌ایم را می‌گیرد، header و Payload را ترکیب می‌کند و آن‌ها را دوباره رمزنگاری می‌کند و بار دیگر با کلیدی که به Signature داده ایم رمزنگاری می‌کند و اگر کسی مقدار رمزگذاری شده را تغییر دهد خطایی دریافت می‌کنیم که به ما نشان می‌دهد Signature معتبر نمی‌باشد. Signatureها نیز ساختاری شبیه به زیر دارند.

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

و در نهایت خروجی رمزگذاری و تمام این‌ها با یک‌دیگر به شکل زیر می‌باشد. PHP - JWT - Header - Pyload - 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 تبدیل کنیم.

$header = json_encode(['alg' => 'HS256', 'typ' => 'JWT']);

در کد بالا ما یک ساختار آرایه آورده‌ایم و همان‌طور که در بالا هم اشاره شد دارای دو کلید به نام‌های typ که نوع آن را مشخص می‌کند و alg که الگوریتم را مشخص می‌کند، حال آرایه را به JSON با تابع json_encode تبدیل کرده‌ایم و آن را در متغیری با نام header ذخیره کرده‌ایم.
$payload = json_encode(['user_id' => 123, 'first_name' => 'Amir', 'last_name' => 'Salehi', 'age' => 18]);

در کد بالا نیز ما مقادیر Payload خود را در ساختار آرایه آوردیم و آن را به JSON با تابع ()json*encode تبدیل کرده‌ایم و در متغیری با نام Payload نگه داشته‌ایم. ساخت رشته Base64Url برای Header و Payload در این قدم ما Header و Payload را به رشته Base64Url رمزنگاری خواهیم کرد اما در PHP ما تابع مخصوصی برای این کار نداریم. یک تابع به اسم ()base64 وجود دارد که با base64Url یک سری تفاوت‌هایی دارد و ما مجبوریم که تغییراتی ایجاد کنیم، برای مثال + ، / و = را با - ، * و رشته خالی (" ") جایگذاری می‌کنیم.
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));

در کد بالا جایگذاری‌ها انجام شده و متغیر header با تابع ()base64_encode رمزنگاری شده است و در متغیر base64UrlHeader ریخته شده است، این متغیر شامل مقدار زیر است.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

و این کار را برای متغیر Payload نیز تکرار می‌کنیم.
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));

اکنون متغیر base64UrlPayload حاوی مقدار زیر است.
eyJ1c2VyX2lkIjoxMjMsImZpcnN0X25hbWUiOiJBbWlyIiwibGFzdF9uYW1lIjoic2FsZWhpIiwiYWdlIjoiMTgifQ

## ساخت Signature

به مهم‌ترین بخش رسیدیم که باید Signature را بسازیم، برای ساخت SIgnature باید از تابع ()hash_hmac و از الگوریتم sha256 استفاده کنیم، لازم به ذکر است که مقادیر درون متغیرهای base64UrlHeader و base64UrlPyload با یک . (dot) از هم دیگر جدا می‌شوند.

$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, 'abC123!', true);

پارامتر اول: تابع ()hash_hmac ما باید نوع الگوریتم هش را تعیین کنیم. پارامتر دوم: داده‌ای که قرار است هش بشود را وارد می‌کنید. پارامتر سوم: یک کلید مخفی برای تولید hmac است. پارامتر چهارم: وقتی که مقدار true را می‌دهیم ، داده‌های باینری را به عنوان خروجی به ما برمی‌گرداند و false هگزیت‌های (hexit) حروف کوچک را به عنوان خروجی برمی‌گرداند.

رمزنگاری Signature تولید شده

حال که Signature خود را تولید کرده‌ایم و آن را درون متغیر signature ذخیره کرده‌ایم باید آن را مانند header و Payload به صورت base64Url رمزنگاری کنیم.

$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));

گام نهایی ساخت JWT

تمامی مقادیری که ما رمزنگاری کردیم و درون متغیرها نگه‌داری کردیم باید به یکدیگر بچسبانیم تا مانند ساختار JWT بشود، برای این کار باید متغیر‌ها را با . (dot) از هم جدا کنیم و درون متغیری با نام jwt بریزیم.

$jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;

خروجی نهایی ما به این صورت است.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.NYlecdiqVuRg0XkWvjFvpLvglmfR1ZT7f8HeDDEoSx8

این آسان‌ترین راهی بود که می‌توانستیم یک JWT بسازیم، کد‌های پایین مجموعه کدهایی هست که ما در بالا نوشته‌ایم.

// Create token header as a JSON string
$header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
// Create token payload as a JSON string
$payload = json*encode(['user_id' => 123]);
// Encode Header to Base64Url String
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '*', ''], base64*encode($header));
// Encode Payload to Base64Url String
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '*', ''], base64*encode($payload));
// Create Signature Hash
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, 'abC123!', true);
// Encode Signature to Base64Url String
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '*', ''], base64_encode($signature));
// Create JWT
$jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
echo $jwt;

# جمع بندی

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