استفاده از JSON Web Tokens با Node.js
یکی از بزرگترین چالش ها در ساخت API احراز هویت است. این یکی از مهمترین سطوح حمله یک API است. احراز هویت مناسب به جلوگیری از تهدیدات امنیتی کمک می کند و تضمین می کند که تنها کاربران مناسب می توانند به داده های مورد نیاز دسترسی داشته باشند.
زمانی که تیم ها با برنامه های سمت سرور کار می کردند، احراز هویت ساده بود. یک اعتبار سنجی ساده جلسه روی سرور برای اطمینان از مجوزهای کاربر برای عملیات کافی بود. با این حال، ظهور API ها تغییر قابل توجهی در این چالش های احراز هویت ایجاد کرده است.
اما با یک API، نمی توانید جلسات را پیاده سازی کنید. شما نمی توانید تضمین کنید که API شما همیشه با استفاده از یک مرورگر وب فراخوانی می شود، پس نمی توانید برای ایمن سازی یک API به کوکی ها اعتماد کنید. یکی از ویژگی های مهم یک API این است که بدون حالت است، به این معنی که هر درخواست ارسال شده به یک API به هیچ درخواست قبلی یا بعدی بستگی ندارد. پس ، شما به رویکردی نیاز دارید که بتواند اطلاعات احراز هویت/مجوز لازم برای تأیید یک درخواست را حمل کند.
یکی از تکنیکهای موثر احراز هویت API استفاده از JSON Web Tokens (JWTs) است. در این مقاله، به جزئیات JWT ها می پردازیم و راهنمای جامعی در مورد نحوه پیاده سازی REST API با استفاده از Node.js با JWT ها به عنوان معیار امنیتی ارائه می دهیم.
خوراکی های کلیدی
پیاده سازی JWT ها برای ارتباط ایمن . این مقاله یک راهنمای عمیق در مورد پیاده سازی JWT ها برای احراز هویت در برنامه های کاربردی وب ارائه می دهد. این شامل تولید، انتقال و تأیید است. با انجام این کار، امنیت کلی API را با جلوگیری از کنترل دسترسی شکسته و با اجازه دادن به افراد مجاز برای دسترسی به داده ها، افزایش می دهد.
کنترل دسترسی مبتنی بر نقش در JWT . این مقاله یک نمای کلی از کنترل دسترسی مبتنی بر نقش را نشان میدهد که در آن نقاط پایانی API خاص برای نقشهای خاص محدود شدهاند. به عنوان مثال، یک مدیر می تواند همه کاربران را مشاهده کند، در حالی که یک مشتری نمی تواند. این در این مقاله با مدیریت ادعاهای سفارشی در توکن JWT اجرا شد.
پیاده سازی JWT در یک REST API . این مقاله یک رویکرد گام به گام برای ایجاد یک REST API ساده با استفاده از Node.js، Express، و کتابخانه jsonwebtoken برای احراز هویت JWT ارائه میکند. این شامل راهاندازی پروژه، نصب کتابخانههای لازم، ایجاد پایگاه داده اولیه کاربر، و پیادهسازی نقاط پایانی ورود و دادههای کاربر است. این فرآیند شامل تولید یک رمز پس از ورود کاربر و تأیید اعتبار این نشانه در درخواستهای بعدی برای مجوز یا رد دسترسی بر اساس نقش کاربر است.
توکن وب JSON (JWT) چیست؟
JSON Web Token (JWT) یک استاندارد باز (RFC 7519) است که روشی برای انتقال اطلاعات بین دو طرف، یک کلاینت و یک سرور، به عنوان یک شی JSON تعریف میکند. توجه به این نکته ضروری است که اطلاعات در حال انتقال بین دو طرف به صورت دیجیتالی با استفاده از امضای خصوصی امضا می شود. پس ، این داده ها تایید شده و ایمن برای استفاده در نظر گرفته می شود.
توجه: به طور معمول، JWT برای ایجاد جریان های احراز هویت و مجوز برای یک API استفاده می شود.
به عنوان مثال، اطلاعاتی که می تواند برای مرتبط کردن کاربر با یک درخواست استفاده شود، اغلب در یک JWT پیچیده می شود. این می تواند شامل شناسه کاربری و نقش باشد و API شما می تواند از این اطلاعات برای تعیین اینکه آیا کاربر ارسال کننده درخواست مجاز به انجام این کار است یا خیر استفاده کند.
چه زمانی باید از JWT استفاده کنید؟
اغلب دو سناریو اصلی وجود دارد که در آنها باید از توکن JWT استفاده کنید.
احراز هویت / مجوز . این یکی از رایج ترین موارد استفاده از JWT است. میتوانید برای تأیید درخواستها در API خود، یک توکن احراز هویت بسازید و اطمینان حاصل کنید که کاربران مجاز اقدامات مجاز را انجام میدهند.
تبادل اطلاعات . شما همچنین می توانید از JWT ها برای تبادل اطلاعات بین طرفین به طور امن استفاده کنید. آنها به عنوان فرم خوبی از داده های معتبر و پذیرفته شده به عنوان JWT ها قابل امضا هستند. برای مثال، با استفاده از جفتهای کلید عمومی/خصوصی، میتوانید مطمئن شوید که فرستنده همان چیزی است که او میگوید . این به شما امکان می دهد تحلیل های بیشتری انجام دهید تا مطمئن شوید اطلاعات شما دستکاری نشده است.
ساختار یک توکن JWT
برای دستیابی به همه عملکردها، توکن JWT به روش خاصی ساختار یافته است. دارای سه جزء کلیدی است:
سرتیتر . هدر از دو بخش تشکیل شده است: نوع توکن، JWT، و الگوریتم امضای مورد استفاده، مانند HMAC SHA256 یا RSA.
ظرفیت ترابری . محموله حاوی ادعاهای شما است. ادعاها اطلاعاتی هستند که نهادی را که توکن صادر میکنید توصیف میکند. به عنوان مثال، اگر توکنی را برای یک کاربر صادر میکردید، ادعاهایی مانند شناسه و نقش کاربر دارید. جدا از آن، یک توکن JWT دارای مجموعه ای استاندارد از ادعاها مانند صادرکننده، زمان صدور، زمان انقضا و موارد دیگر است.
امضاء . این چیزی است که شما باید ایجاد کنید. برای ایجاد امضا، باید هدر کدگذاری شده، بار رمزگذاری شده، یک راز و الگوریتم مشخص شده در هدر را بگیرید و آن را امضا کنید. این کار برای اطمینان از عدم تغییر پیام در طول مسیر انجام می شود.
توجه: توکن JWT شما یک رشته plan base64 است که از این سه جزء تشکیل شده است که هر جزء با استفاده از یک .
.
به عنوان مثال، یک توکن ساده ممکن است چیزی شبیه به این باشد:
header.payload.signature
علاوه بر این، رمز رمزگشایی شده شما چیزی شبیه به تصویر زیر میخواهد.
که نسخه های رمزگذاری شده و رمزگشایی شده JWT را نشان می دهد" loading="lazy">
همانطور که می بینید، هدر، محموله و امضا رمزگشایی شده و در بالا نشان داده شده است.
جریان فرآیند یک JWT
اکنون، هنگامی که یک API با JWT می سازید، باید موارد زیر را در نظر بگیرید:
ورود به سیستم در
تولید توکن
اعتبار سنجی توکن
این چیزی شبیه چیزی است که در زیر نشان داده شده است.
که سطح کار کاربر، برنامه مشتری و API را نشان میدهد و نشان میدهد که چگونه JWT در آن جا میشود." loading="lazy">
چرخه زمانی شروع می شود که کاربر برای اولین بار درخواستی برای ورود به API ارسال می کند. آنها یک نام کاربری و یک رمز عبور ارائه می دهند. API شما تأیید می کند که آیا اعتبارنامه ها معتبر هستند یا خیر، و در این صورت، یک توکن JWT برای کاربر ایجاد می کند.
در مرحله بعد، کاربر شما در هر درخواستی که اجرا می کند، این نشانه را در سرصفحه درخواست - Authorization
- به عنوان یک نشانه حامل قرار می دهد. API شما باید برای همه درخواستها به هدر درخواست نگاه کند و رمزگشایی و اعتبارسنجی کند تا درخواست را تأیید کند.
رعایت این فرآیند هنگام کار با JWT ضروری است. اگر هدر شما توکن JWT را نداشته باشد، API درخواست را رد خواهد کرد.
ساخت REST API با JWT
ساختن یک API با احراز هویت JWT ساده تر از آن چیزی است که به نظر می رسد. کتابخانههای زیادی در دسترس هستند که فرآیند تولید توکن و اعتبارسنجی را از طریق روشهای ساده API انجام میدهند.
پس ، اجازه دهید یک REST API ساده با احراز هویت JWT بسازیم.
برای انجام این کار، اجازه دهید ابتدا یک پروژه را با استفاده از دستور بوت استرپ کنیم:
npm init
توجه: مطمئن شوید که با تنظیمات پیش فرض ادامه دهید.
سپس، بیایید کتابخانه JWT را که با آن کار می کنیم نصب کنیم. بیایید از کتابخانه jsonwebtoken برای ایجاد و مدیریت توکن های JWT استفاده کنیم.
توجه: من این کتابخانه را انتخاب کردم زیرا اغلب در GitHub نگهداری می شود و بیش از 14 میلیون بار در هفته دارد.
پس ، کتابخانه را با استفاده از دستور نصب کنید:
npm i jsonwebtoken
بعد، اجازه دهید Express را برای ساختن API نصب کنیم. برای انجام این کار، دستور را اجرا کنید:
// express - to build the api // cors - to enable cross origin requests // body-parser - to parse the body as JSON npm i express cors body-parser
بعد، بیایید یک فایل database.js
ایجاد کنیم. از آنجایی که ما در اینجا به شدت بر روی JWT ها تمرکز می کنیم، من یک پایگاه داده را نمی چرخانم، بلکه یک پایگاه داده درون کد کاربران را حفظ می کنم. پس ، فایل database.js
خود را باز کنید و کد زیر را وارد کنید:
const users = [ { id : '1' , name : 'Lakindu' , username : 'lak' , password : '1234' , role : 'customer' } , { id : '2' , name : 'David' , username : 'david' , password : '1234' , role : 'customer' } , { id : '3' , name : 'John' , username : 'john' , password : '1234' , role : 'customer' } , { id : '4' , name : 'Nishanthan' , username : 'nishanthan' , password : '1234' , role : 'customer' } , { id : '5' , name : 'Pasindu' , username : 'pasindu' , password : '1234' , role : 'customer' } , { id : '6' , name : 'Sahan' , username : 'sahan' , password : '1234' , role : 'admin' } , ] module . exports = { users }
همانطور که می بینید، ما فهرست ی از کاربرانی که قرار است به API ما دسترسی داشته باشند تعریف کرده ایم.
توجه: اگر این را در سطح تولید میسازید، توصیه میکنم از چیزی مانند Amazon Cognito برای مدیریت کاربران خود استفاده کنید یا برای ذخیره رمزهای عبور هش کنید.
سپس یک فایل index.js
برای تعریف API ایجاد کنید. فایل index.js
را باز کنید و کد زیر را وارد کنید:
app . post ( '/login' , ( req , res ) => { const { username , password } = req . body ; const user = users . find ( ( user ) => user . username === username ) ; if ( ! user || user . password !== password ) { res . status ( 400 ) ; res . send ( { message : 'Invalid username or password' } ) return ; } if ( user . password === password ) { const token = jwt . sign ( { role : user . role , } , tokenSecret , { algorithm : 'HS256' , expiresIn : '5m' , issuer : 'my-api' , subject : user . id } ) res . send ( { token } ) ; return ; } } ) ;
ما اکنون سه نقطه پایانی API را پیاده سازی کرده ایم:
POST / ورود به سیستم . این مسیر تلاش می کند تا یک کاربر را احراز هویت کند. انتظار دارد نام کاربری و رمز عبور در بدنه درخواست وجود داشته باشد. کنترل کننده کاربری را با نام کاربری منطبق در آرایه کاربران جستجو می کند. اگر کاربری پیدا نشد یا رمز عبور مطابقت نداشت، با یک کد وضعیت 400 و یک پیام خطا پاسخ میدهد. اگر کاربر پیدا شود، با پیامی پاسخ می دهد که نشان دهنده ورود موفقیت آمیز است.
GET /users . این مسیر با یک رشته JSON حاوی تمام کاربران پاسخ می دهد.
GET /users/:userId . این یک userId
از پارامترهای مسیر بازیابی می کند و از آن برای یافتن یک کاربر در آرایه کاربران استفاده می کند.
اکنون، در مورد ما، میتوانیم از JWT برای چندین مورد استفاده کنیم:
هنگام ورود به سیستم، یک توکن JWT ایجاد کنید و آن را به کاربر برگردانید.
هنگام درخواست برای API کاربران، آنها می توانند توکن مجوز را وارد کنند. به عنوان مثال، فقط یک مدیر باید بتواند یک کاربر را با شناسه واکشی کند و همه کاربران را دریافت کند. مشتریان نباید قادر به انجام این کار باشند.
پس ، اجازه دهید نقطه پایانی ورود به سیستم را برای تولید یک نشانه به روز کنیم:
const jwt = require ( 'jsonwebtoken' ) ; app . post ( '/login' , ( req , res ) => { const { username , password } = req . body ; const user = users . find ( ( user ) => user . username === username ) ; if ( ! user || user . password !== password ) { res . status ( 400 ) ; res . send ( { message : 'Invalid username or password' } ) return ; } if ( user . password === password ) { const token = jwt . sign ( { role : user . role , } , tokenSecret , { algorithm : 'HS256' , expiresIn : '5m' , issuer : 'my-api' , subject : user . id } ) res . send ( { token } ) ; return ; } } ) ;
همانطور که می بینید، ما نقطه پایانی ورود به سیستم را به روز کرده ایم تا از کتابخانه jsonwebtoken
برای ایجاد یک نشانه امضا شده استفاده کنیم. این توکن از الگوریتم HMAC SHA-256 استفاده میکند و در عرض پنج دقیقه منقضی میشود و با userId
برای موضوع صادر میشود. این به این معنی است که توکن برای یک کاربر خاص استفاده می شود.
علاوه بر این، یک tokenSecret
را نیز ارسال کرده ایم. این یک کلید مخفی است که برای رمزگشایی رمز برای تمام درخواستهای آینده استفاده میشود. این همچنین یک لایه امنیتی به توکن شما اضافه می کند، که در آن تمام نشانه هایی که توسط کلید مخفی شما رمزگشایی نمی شوند، می توانند دستکاری شده در نظر گرفته شوند.
پس ، هنگامی که نقطه پایانی ورود خود را اجرا می کنید، باید یک توکن به عنوان خروجی دریافت کنید، همانطور که در تصویر زیر مشاهده می کنید.
اگر توکن را روی اشکالزدای آنلاین jwt.io قرار دهید، میتوانید آپشن های تصویر زیر را ببینید.
که توسط دیباگر آنلاین jwt.io رمزگشایی شده است" loading="lazy">
بیشتر بخوانید
Adobe’s Firefly Services بیش از 20 API مولد و خلاقانه را در اختیار توسعه دهندگان قرار می دهد.
اکنون، اجازه دهید User API را برای اعتبارسنجی توکن به روز کنیم. برای انجام این کار، اجازه دهید یک تابع کنترل دسترسی مبتنی بر نقش (RBAC) ایجاد کنیم:
const validateRequest = ( requiredRole ) => { return ( req , res , next ) => { const { authorization } = req . headers const token = authorization . substring ( 'Bearer ' . length ) ; try { const { exp , iss , role } = jwt . verify ( token , tokenSecret ) ; if ( iss === 'my-api' && exp < Date . now ( ) && role === requiredRole ) { next ( ) ; return ; } } catch ( err ) { res . sendStatus ( 403 ) ; return ; } } }
در اینجا، ما یک تابع مرتبه بالاتر ایجاد کردهایم که یک پارامتر requiredRole
را میگیرد و یک تابع میانافزار را برمیگرداند. این طراحی به ما امکان می دهد میان افزار متناسب با نقش مورد نیاز برای دسترسی به مسیرهای خاص ایجاد کنیم. در نتیجه، تابع برگردانده شده میان افزار سازگار با Express است. پارامترهای req (درخواست)، res (پاسخ)، و next (عملکرد فراخوانی میان افزار بعدی) را می گیرد.
این تابع برگشتی با انجام کارهای زیر، توکن JWT را تأیید می کند:
استخراج توکن با استخراج JWT از سربرگ مجوز درخواست ورودی شروع می شود. قالب مورد انتظار هدر Bearer [token] است، پس پیشوند 'Bearer '
را حذف می کند تا نشانه را جدا کند.
رمزگشایی توکن سپس رمز با استفاده از jwt.decode(token)
رمزگشایی می شود که JWT را تجزیه می کند و بار آن را بدون تأیید امضا استخراج می کند. پیش بینی می شود محموله حداقل شامل سه ادعا باشد: exp
(زمان انقضا)، iss
(صادرکننده)، و role
(نقش کاربر).
بررسی های اعتبار سنجی میان افزار تحلیل های زیر را روی توکن رمزگشایی شده انجام می دهد:
صادر کننده . تأیید میکند که ادعای iss
(صادرکننده) با 'my-api'
مطابقت دارد، که نشان میدهد نشانه توسط مرجع مورد انتظار صادر شده است.
انقضاء . تحلیل می کند که آیا exp
(زمان انقضا) کمتر از زمان فعلی است ( Date.now()
)، که به این معنی است که توکن منقضی شده است.
نقش . ادعای نقش در توکن را با پارامتر requiredRole
که به validateRequest
ارسال شده است مقایسه میکند. این تضمین می کند که کاربر نقش مناسبی برای درخواست دارد.
توجه: اگر همه چک ها تایید شوند (صادر کننده صحیح است، توکن منقضی نشده است، و کاربر نقش مورد نیاز را دارد)، next() را فراخوانی می کند تا به میان افزار بعدی یا کنترل کننده مسیر بروید. در صورت عدم موفقیت هر چک، کد وضعیت 403 Forbidden را به عنوان پاسخ ارسال می کند که نشان می دهد درخواست غیرمجاز است.
در مرحله بعد، می توانید تابع میان افزار را به مسیرهای خود اضافه کنید و نقش مورد نیاز برای دسترسی به مسیر را مشخص کنید:
app . get ( '/users' , validateRequest ( 'admin' ) , ( req , res ) => { res . send ( JSON . stringify ( { users } ) ) } ) ; app . get ( '/users/:userId' , validateRequest ( 'admin' ) , ( req , res ) => { const { params } = req ; const { userId } = params ; console . log ( { userId } ) ; const user = users . find ( ( user ) => user . id === userId ) ; if ( ! user ) { res . sendStatus ( 404 ) return ; } res . send ( { user } ) } ) ;
همانطور که در بالا نشان داده شده است، هر دو مسیر محافظت می شوند به طوری که فقط مدیر می تواند به آن دسترسی داشته باشد. اکنون، اگر به عنوان مشتری یا با یک توکن نامعتبر سعی در دسترسی به مسیر دارید، باید خروجی را در تصویر زیر مشاهده کنید.
که این برنامه را نشان میدهد در حال بازگشت به حالت ممنوعه است" loading="lazy">
اما، اگر توکن شما معتبر است، باید خروجی تصویر زیر را ببینید.
بسته بندی
و این تقریباً برای این مقاله است. شما با موفقیت یک REST API را با استفاده از احراز هویت/مجوز مبتنی بر JWT ساخته اید.
در مرحله بعد، میتوانید از این توکن در برنامههای سمت کلاینت خود مانند Angular یا React استفاده کنید و توکن را در تمام درخواستهای API ارسال کنید تا اطمینان حاصل کنید که frontend شما میتواند با API با موفقیت ارتباط برقرار کند.
اگر میخواهید کد را تحلیل کنید، از مخزن GitHub من یا این نسخه نمایشی CodeSandbox دیدن کنید.
ممنون که خواندید.
سوالات متداول (سؤالات متداول) در مورد استفاده از توکن های وب JSON در Node.js
JSON Web Token (JWT) ابزاری فشرده و ایمن برای نشان دادن ادعاهایی است که باید بین دو طرف منتقل شود. JWT ها برای انتقال ایمن اطلاعات بین یک کلاینت و یک سرور به عنوان یک شی JSON استفاده می شوند که می توان آن را تأیید کرد و به آن اعتماد کرد زیرا به صورت دیجیتالی امضا شده است.
چگونه انقضای JWT را مدیریت کنم؟
JWT ها دارای یک فیلد انقضا (exp) هستند که تعیین می کند چه زمانی توکن دیگر معتبر نیست. برای رسیدگی به انقضا در Node.js، می توانید از روشی که توسط کتابخانه تولید توکن استفاده کرده اید استفاده کنید.
آیا باید JWT ها را در کوکی ها یا ذخیره سازی محلی ذخیره کنم؟
انتخاب بین ذخیره سازی JWT در کوکی ها یا ذخیره سازی محلی به نیازهای خاص و ملاحظات امنیتی برنامه شما بستگی دارد.
وقتی کوکی ها به درستی پیکربندی شوند، عموماً ایمن تر هستند (مانند HttpOnly، Secure، SameSite)، زیرا نسبت به ذخیره سازی محلی کمتر مستعد حملات XSS هستند. با این حال، کوکی ها می توانند در برابر حملات CSRF آسیب پذیر باشند. استفاده از فضای ذخیرهسازی محلی، توکن شما را مستعد حملات XSS میکند اما نه حملات CSRF.
پس ، مهم است که این ملاحظات را بسنجید و تدابیر امنیتی بیشتری را بدون توجه به تکنیکی که اتخاذ میکنید، اعمال کنید.
بله، JWT ها را می توان با صدور یک توکن جدید برای مشتری قبل از منقضی شدن توکن قدیمی، به روز کرد. این معمولاً شامل داشتن یک نشانه تازه سازی جداگانه است که صرفاً برای به دست آوردن نشانه های دسترسی جدید استفاده می شود.
توکن رفرش به طور ایمن در سرور ذخیره می شود و در کنار توکن دسترسی برای مشتری ارسال می شود. هنگامی که نشانه دسترسی در شرف منقضی شدن است، مشتری می تواند با استفاده از نشانه رفرش، یک نشانه جدید درخواست کند.
چگونه می توانم یک رمز وب JSON برای مشتری ارسال کنم؟
پس از ایجاد یک توکن، می توانید آن را در پاسخ به یک درخواست ورود موفق برای مشتری ارسال کنید. سپس مشتری باید توکن را ذخیره کند و آن را در هدر مجوز درخواستهای بعدی قرار دهد.
چگونه از مسیرها با توکن های وب JSON در Node.js محافظت کنم؟
برای محافظت از مسیرها، میتوانید یک تابع میانافزار ایجاد کنید که توکن موجود در هدر مجوز درخواست را تأیید میکند. اگر توکن معتبر باشد، تابع میانافزار باید next
فراخوانی کند تا درخواست ادامه یابد. اگر توکن نامعتبر باشد، تابع میان افزار باید پاسخی با کد وضعیت خطا ارسال کند.
چگونه خطاها را هنگام تأیید یک توکن کنترل کنم؟
هنگام تأیید یک نشانه، متد verify
تابع callback را با خطا در صورت نامعتبر بودن رمز فراخوانی می کند. شما می توانید این خطا را برای ارسال پاسخ با کد وضعیت و پیام مناسب مدیریت کنید. به عنوان مثال، اگر خطا به این دلیل است که رمز منقضی شده است، ممکن است یک کد وضعیت غیرمجاز 401 را با پیامی مبنی بر «زمان منقضی شده» ارسال کنید. لطفا دوباره وارد شوید."
ارسال نظر