چند وقتی است که بحث توکن های JWT برای احراز هویت داغ شده است؛ به دلایل متعدد زیادی که بسیار هم موجه هستند. نیما تصمیم گرفته که مشقی به مهیار بدهد که این شیوهی احراز هویت را در ریاکت به واسطهی یک هوک پیاده کند. همچنین نیما زحمت پیادهسازی سروری که از این شیوهی احراز هویت پشتیبانی میکند را قبول کرده است.
این سرور وظیفهی احراز هویت کاربران را برعهده دارد و همواره توکنی که برمیگرداند زمان انقضای مشخصی دارد، به این معنا که پس از زمان تعیینشده، دیگر توکن قابل استفاده نیست. از این توکن تحت عنوان Access Token نیز یاد میشود. در کنار این توکن، یک توکن برای دریافت مجدد توکن جدید و معتبر نیز در اختیار کاربر قرار میدهد. از این توکن تحت عنوان Refresh Token یاد میشود. با ارسال این توکن به سرور، در صورت معتبر بودن، یک Access Token و همچنین Refresh Token جدید در اختیار کاربر قرار میگیرد.
مهیار نه تنها بسیار در امر زمانبندی بسیار نامنظم است، بلکه بسیار تنبل و پر مشغله نیز هست! به همین دلیل، تصمیم گرفته است که از شما در انجام این تمرین کمک بگیرد.
جزئیات پروژه
پروژهی اولیه را از این لینک دانلود کنید.
ساختار فایلهای پروژه
jwt-auth
├── package.json
├── package-lock.json
├── public
│ └── index.html
├── README.md
├── server
│ ├── index.js
│ └── requests.rest
└── src
├── App.css
├── App.js
├── Components
│ └── ...
├── Hooks
│ └── useJWT.js
├── index.js
├── Pages
│ ├── Login.jsx
│ └── Profile.jsx
├── setupTests.js
└── __tests__
└── sample.test.js
راهاندازی پروژه
-
پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
-
با اجرای دستور
npm i
پکیجهای موردنیاز را نصب کنید. -
با اجرای دستور
node server/index.js
سرور را اجرا کنید. -
با اجرای دستور
npm start
پروژه را اجرا کنید.
هوک useJWT
نیما از مهیار خواسته است که یک هوک برای این شیوهی احراز هویت طراحی کند که بتوان از آن در پروژههای بعدی نیز استفاده کرد. جزییات این هوک که نامش useJWT
است بهشرح زیر است:
این هوک باید چهار تابع با نامهای زیر برگرداند. در ادامه به جزئیات هر یک از آنها خواهیم پرداخت:
const { login, logout, refreshToken, sendPostRequest } = useJWT()
نکتهای که نیما از مهیار خواسته است در پیادهسازی این توابع به خاطر داشته باشد، این است که بتوان از همهی آنها به شکل زیر استفاده کرد:
f(...args).then(data => {}}.catch(err => {});
تابع login
این تابع بهترتیب دو آرگومان email
و password
دریافت میکند و آنها را در قالب یک شی و متد POST به آدرس http://127.0.0.1:4000/api/login
ارسال میکند. در صورتی که رمز و ایمیل کاربر درست وارد شده باشد، سرور یک شی بهصورت زیر برمیگرداند:
{
"data": {
"access": "ACCESS_TOKEN",
"refresh": "REFRESH_TOKEN"
}
}
همچنین مقادیر access
و refresh
باید بهصورت مجزا و با همان نام در فضای محلی مرورگر (localStorage
) ذخیره شوند (مقدار access
در کلید access
و مقدار refresh
در کلید refresh
ذخیره شود).
همچنین توکنهای دریافت شده را باید در قالب یک شی بهصورت زیر برگرداند:
{
access: "ACCESS_TOKEN",
refresh: "REFRESH_TOKEN"
}
تابع refreshToken
این تابع هیچ آرگومانی دریافت نمیکند. وظیفهی اصلی این تابع، ارسال Refresh Token ذخیرهشده در مرورگر در قالب بدنهی یک درخواست POST به آدرس http://127.0.0.1:4000/api/token
است. در صورتی که توکن معتبر باشد، سرور یک شی بهصورت زیر برمیگرداند:
{
data: {
"access": "ACCESS_TOKEN",
"refresh": "REFRESH_TOKEN"
}
}
مقادیر access
و refresh
به صورت مجزا و با همان نام باید در فضای محلی مرورگر (localStorage
) ذخیره شوند. همچنین بدنه درخواست ارسالی باید به صورت زیر باشد:
{
"token": "REFRESH_TOKEN"
}
علاوه بر آن، این تابع باید توکنهای دریافتشده را در قالب یک شی بهصورت زیر برگرداند:
{
access: "ACCESS_TOKEN",
refresh: "REFRESH_TOKEN"
}
تابع logout
این تابع هیچ آرگومانی دریافت نمیکند. وظیفهی اصلی این تابع، حذف Access Token و Refresh Token از localStorage
است. همچنین این تابع باید مقدار Refresh Token ذخیرهشده در localStorage
را در قالب بدنهی یک درخواست از نوع DELETE به آدرس http://127.0.0.1:4000/api/logout
(در قالب کلیدی تحت عنوان token
) ارسال کند:
const logout = () => { /* TODO: Implement */ }
تضمین میشود که در این بخش همواره کد پاسخ برابر با 204
است.
تابع sendPostRequest
وظیفهی اصلی این تابع، ارسال یک درخواست از نوع POST به سرور است. این تابع به عنوان ورودی، ابتدا مسیر درخواست که از نوع رشته است را دریافت میکند و به عنوان آرگومان دوم، دادهای که قرار است در بدنه درخواست ارسال شود را دریافت میکند.
از آنجایی که این تابع به منظور راحتتر شدن ارسال درخواست به مسیرهای که نیاز به توکن دارند طراحی شده است، دو هدف اصلی را دنبال میکند:
- در هدرهای ارسالی باید Access Token با کلید
jwt
در هدر قرار بگیرد. - در صورتی که درخواست اول به دلیل منقضی شدن Access Token ناموفق شود، از سرور درخواست یک توکن جدید میکند و درخواست اصلی را مجدداً با توکن جدید ارسال میکند. لازم به ذکر است این عمل تنها یک بار صورت میگیرد (در صورتی که در دفعهی دوم هم درخواست ناموفق باشد، نباید خطایی پرتاب شود یا کار دیگری صورت گیرد).
- در صورتی که درخواست موفقیتآمیز باشد، نتیجهی درخواست را برمیگرداند.
کامپوننت src/Pages/Login.jsx
این کامپوننت یک صفحهی ورود است. بخش return
که بیانگر ساختار صفحه است از قبل نوشته شده است، اما موراد زیر باید تکمیل شوند:
- هنگام فشرده شدن
LoginButton
، ایمیل و رمز عبور با استفاده از متد مربوطهی هوک طراحیشده به سرور ارسال شوند. - درصورت ورود موفق کاربر، هدایت به آدرس
/
صورت گیرد. - در صورتی که احراز هویت ناموفق باشد، باید دو کامپوننت
NoRobot
وErrorMessage
نمایش داده شوند. همچنین مقدار ورودیpassword
باید خالی باشد اما مقدار ورودیemail
همان مقدار قبلی باشد. - دکمهی
LoginButton
باید در صورتی فعال باشد که ایمیل و رمز عبور هر دو وارد شده باشند. همچنین ایمیل واردشده باید طبق فرمتuser@test.com
باشد و تیک مربوط بهNoRobot
نیز باید فعال باشد.
کامپوننت src/Pages/Profile.jsx
- هنگام بارگذاری این کامپوننت، ابتدا باید بررسی شود که کاربر توکنی دارد یا خیر. در صورت داشتن توکن، یک درخواست به آدرس
http://127.0.0.1:4000/api/user
ارسال شود در غیر این صورت کاربر به صفحهlogin/
هدایت شود. پاسخ سرور بهصورت زیر خواهد بود:
{
data: {
"user": {...values}
}
}
در اینصورت، شما باید مقدار استیت user
را برابر با data
دریافت شده قرار دهید. در غیر اینصورت (یعنی اگر درخواست ناموفق بود)، کاربر باید به آدرس /login
هدایت شود
- همچنین در صورت فشرده شدن دکمهی
LogoutButton
باید روند خروج کاربر صورت پذیرد و کاربر به آدرس/login
هدایت شود.
نکات
- در این پروژه از React Router با نسخه
v6
یعنی جدیدترین نسخه، استفاده شده است. - آدرس درخواستها باید دقیقاً مطابق با آدرسهای ذکرشده در صورت سؤال باشد.
- دادهها باید بهصورت JSON به سرور ارسال شوند.
- برای انجام این پروژه میتوانید از
axios
استفاده کنید. - شما تنها مجاز به اعمال تغییرات در فایلهای
useJWT.js
،Login.jsx
وProfile.jsx
هستید.
آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشهی src
را زیپ کرده و آپلود کنید.
ارسال پاسخ برای این سؤال