ما هو JWT؟ ستأخذك هذه المقالة لفهم JWT، وتقديم تطبيق JWT في العقدة، ومزايا وعيوب JWT، وآمل أن تكون مفيدة للجميع!
JWT هو اختصار لـ JSON Web Token ، وهو حل مصادقة في بيئة تطبيقات الشبكة، وفي آلية المصادقة التقليدية، لا يتعدى ذلك الخطوات التالية:
1. يرسل المستخدم كلمة مرور الحساب إلى الخادم؛ 2. بعد أن يتحقق الخادم من كلمة مرور الحساب، سيحفظ بعض المعلومات المتعلقة بالمستخدم أو أدوار المستخدم أو وقت انتهاء الصلاحية وما إلى ذلك في الجلسة الحالية؛ 3. يمنح الخادم المستخدم session_id ويكتبه في ملف تعريف الارتباط الخاص بالمستخدم أو يحفظه العميل محليًا؛ 4. في كل مرة يطلب المستخدم خدمة ما، عليه إحضار معرف الجلسة هذا، ربما من خلال ملفات تعريف الارتباط أو طرق أخرى؛ 5. بعد أن يستقبله الخادم، يعود إلى قاعدة البيانات للاستعلام عن معرف الجلسة الحالي والتحقق مما إذا كان المستخدم لديه الإذن أم لا؛
تتمثل ميزة هذا النموذج في أنه يمكن للخادم إنهاء أذونات المستخدم في أي وقت، ويمكنه الانتقال إلى قاعدة البيانات لتعديل أو حذف معلومات جلسة المستخدم الحالية. ولكن هناك أيضًا عيبًا، وهو أنه إذا كانت مجموعة خوادم، فستحتاج جميع الأجهزة إلى مشاركة معلومات الجلسة للتأكد من أن كل خادم يمكنه الحصول على نفس معلومات تخزين الجلسة. وعلى الرغم من إمكانية حل هذه المشكلات، إلا أن حجم العمل كبير.
تتمثل ميزة حل JWT في أنه لا يتم حفظ هذه المعلومات، ويتم حفظ بيانات الرمز المميز على العميل في كل مرة يتم فيها قبول الطلب، فهو يحتاج فقط إلى التحقق منه.
دعونا نتحدث بإيجاز عن مبدأ JWT في الواقع، عندما يرسل العميل طلبًا للمصادقة، سيقوم الخادم بإنشاء كائن JSON بعد مصادقة المستخدم، والذي ربما يتضمن معلومات مثل "من أنت، ماذا تفعل، وما إلى ذلك". ., وقت انتهاء الصلاحية "، المهم أنه يجب أن يكون هناك وقت انتهاء الصلاحية؛ الصيغة العامة هي:
{ اسم المستخدم: "سلسلة اللص إيه"، الدور: "مزارع الكود العالمي"، وقت الانتهاء: "20 مايو 2022" }
ولكن لن يتم تمريرها إليك بهذه الطريقة السطحية، بل سيتم توقيعها وإرسالها من خلال خوارزمية توقيع قابلة للعكس بناءً على خوارزمية التوقيع المحددة وبعض المعلومات حول الحمولة التي أرسلتها :
كما يتبين من الصورة، فإن المعلومات التي تم إرجاعها مقسمة تقريبًا إلى ثلاثة أجزاء، والجانب الأيسر هو النتيجة بعد التوقيع، وهي النتيجة التي يتم إرجاعها إلى العميل مفصولة بـ "نقاط"، على التوالي، هناك تطابق واحد لواحد بين الألوان الثلاثة الأحمر والأرجواني والسماوي:
الجزء الأحمر الأول هو الرأس. يحدد الرأس بشكل أساسي الطريقة. خوارزمية التوقيع في الصورة ( الافتراضي HS256 ) هي HMAC مع SHA-256 وهي خوارزمية متماثلة مشتركة بين الطرفين معرف الحقل من نوع JWT؛
الجزء الأرجواني الثاني، الحمولة، هو كائن JSON، وهو البيانات الفعلية التي سيتم إرسالها. هناك سبعة حقول رسمية يمكن استخدامها:
إصدار (المصدر): المصدر
exp (وقت انتهاء الصلاحية): وقت انتهاء الصلاحية
فرعي (موضوع): موضوع
أود (الجمهور): الجمهور
nbf (ليس قبل): الوقت الفعلي
iat (صدر في): وقت الإصدار
jti (معرف JWT): الرقم
بالإضافة إلى هذه الحقول، يمكنك أيضًا إنشاء بعض الحقول المخصصة نظرًا لأن JWT غير مشفر بشكل افتراضي، فحاول أن تكون حريصًا على عدم استخدام بعض البيانات الحساسة عند استخدامه.
الجزء الثالث هو توقيع Signature
. هذا الجزء عبارة عن مفتاح سري تحدده أنت وهو موجود فقط على الخادم، ثم يستخدم الخوارزمية المحددة في الرأس لتسجيل الدخول من خلال طريقة التوقيع التالية.
دعونا نختبر الاستخدام المحدد أدناه:
الخطوة 1: نحتاج إلى إنشاء مشروع Nodejs؛ وتهيئة المشروع من خلال npm init -y
؛ ثم نحتاج إلى تثبيت التبعيات، بما في ذلك express
و jsonwebtoken
و nodemon
:
$ npm أعبر عن jsonwebtokenNodemon
ثم أضف الأمر nodemon app.js
في حقل scripts
في package.json
:
"البرامج النصية": { "start": "nodemon app.js" },
الخطوة 2: تهيئة تطبيق العقدة وإنشاء ملف app.js في الدليل الجذر؛
// app.js const Express = require("express"); تطبيق const = Express(); app.use(express.json()); app.listen(3000, () => { console.log(3000 + "listening..."); // الاستماع إلى المنفذ 3000});
الخطوة 3: تقديم تبعية jsonwebtoken
وإنشاء المفتاح الخاص للواجهة والخادم؛
// app.js //... const jwt = require("jsonwebtoken"); const jwtKey = "~!@#$%^&*()+،"; // ...
jwtKey
هنا هو مفتاحنا الخاص المخصص الذي يتم حفظه فقط في الخادم. بعد ذلك، بدأنا في كتابة واجهة /login لتسجيل الدخول، وأنشأنا قاعدة بيانات محاكاة محلية للتحقق، وقمنا بتنفيذ ذلك من خلال طريقة jwt.sign
. التحقق من التوقيع:
// app.js قاعدة بيانات ثابتة = { اسم المستخدم: "اسم المستخدم"، كلمة المرور: "كلمة المرور"، }; app.post("/login", (req, res) => { const { اسم المستخدم وكلمة المرور } = req.body; إذا (اسم المستخدم === قاعدة البيانات. اسم المستخدم && كلمة المرور === قاعدة البيانات. كلمة المرور) { jwt.sign( { اسم المستخدم، }, مفتاح, { تنتهي في: "30S"، }, (_، رمز مميز) => { res.json({ اسم المستخدم، الرسالة: "تم تسجيل الدخول بنجاح"، رمز مميز, }); } ); } });
في الكود أعلاه، أنشأنا متغير database
لمحاكاة إنشاء حساب محلي وقاعدة بيانات كلمة المرور للتحقق من تسجيل الدخول؛ ثم أنشأنا واجهة post
/login
بعد التحقق من تطابق الحساب وكلمة المرور تمامًا، وقمنا باستيرادها من خلال jsonwebtoken
package. استخدم طريقة sign
ضمن كائن jwt
للتوقيع. تحتوي هذه الطريقة على ثلاثة توقيعات للواجهة:
علامة وظيفة التصدير ( الحمولة: سلسلة | كائن عازل. SecretOrPrivateKey: سر، خيارات؟: خيارات التوقيع، ): خيط؛ علامة وظيفة التصدير ( الحمولة: سلسلة | كائن عازل. SecretOrPrivateKey: سر، رد الاتصال: تسجيل رد الاتصال، ): فارغ؛ علامة وظيفة التصدير ( الحمولة: سلسلة | كائن عازل. SecretOrPrivateKey: سر، الخيارات: خيارات التوقيع، رد الاتصال: تسجيل رد الاتصال، ): فارغ؛
يتم استخدام طريقة التحميل الزائد للوظيفة هنا لتنفيذ الواجهة، وسنقوم بتنفيذ توقيع object
الأخير هنا. يمكن أن تكون المعلمة الأولى نوع كائن مخصص، أو نوع Buffer
، أو نوع string
مباشرة وتخصيص بعض الحقول، لأن JWT سيوقع أيضًا هذه البيانات عند التوقيع، ومع ذلك، تجدر الإشارة إلى أنه يجب عليك محاولة عدم استخدام البيانات الحساسة هنا لأن JWT غير مشفر بشكل افتراضي ، مما يضمن أن البيانات لم يتم التلاعب به، وتسمى عملية التحقق من التوقيع بالتحقق .
بالطبع، يمكنك أيضًا تشفير الرمز الأصلي ثم إرساله؛
المعلمة الثانية: هي المفتاح السري الذي نحفظه على الخادم للتوقيع، عادةً في وضع خادم العميل، يستخدم JWS خوارزمية HS256 المقدمة من JWA بالإضافة إلى المفتاح. قد تحتاج خدمات متعددة إلى التحقق من JWT. إذا تم حفظ المفتاح في كل خدمة، فسيتم تقليل الأمان بشكل كبير. يجب أن تعلم أنه بمجرد تسريب المفتاح، يمكن لأي شخص تزوير JWT.
المعلمة الثالثة: هي خيار التوقيع SignOptions
، توقيع الواجهة:
واجهة التصدير SignOptions { خوارزمية ؟: خوارزمية |. معرف المفتاح؟: سلسلة |. تنتهي؟: رقم السلسلة |. /** يتم التعبير عنها بالثواني أو سلسلة تصف فترة زمنية [zeit/ms](https://github.com/zeit/ms.js، على سبيل المثال: 60، "يومان"، "10h"، "7d"). */ ليس قبل؟: رقم السلسلة |. الجمهور ؟: سلسلة |. سلسلة [] |. الموضوع؟: سلسلة |. المُصدر ؟: سلسلة |. jwtid ؟: سلسلة غير محددة؛ mutatePayload ؟: منطقية |. noTimestamp ؟: منطقي |. رأس ؟: JwtHeader |. الترميز ؟: سلسلة |. }
نستخدم هنا حقل expiresIn
لتحديد وقت التقادم. يرجى الرجوع إلى هذا المستند للتعرف على طرق الاستخدام؛
المعلمة الرابعة هي رد الاتصال، المعلمة الثانية لرد الاتصال هي token
الذي أنشأناه من خلال التوقيع، وأخيرًا، يتم إرجاع هذا token
إلى الواجهة الأمامية بحيث يمكن تخزينه محليًا على الواجهة الأمامية وإحضاره إلى الخادم للتحقق منه. على كل طلب.
بعد ذلك، دعونا نتحقق من هذه الواجهة: لقد قمت بتثبيت المكون الإضافي REST Client في vscode، ثم قمت بإنشاء ملف request.http
في الدليل الجذر، وكتبت المعلومات المطلوبة في الملف:
نشر http://localhost:3000/login نوع المحتوى: التطبيق/json { "اسم المستخدم": "اسم المستخدم"، "كلمة المرور": "كلمة المرور" }
ثم قم بتنفيذ الأمر npm run start
في سطر الأوامر لبدء الخدمة، ثم انقر فوق الزر Send Request
أعلى ملف requset.http
لإرسال الطلب:
بعد نجاح الطلب، ستظهر لك رسالة الرد كالتالي:
حقل token
هو token
الذي تم إنشاؤه بواسطة JWT الخاص بنا؛
دعونا نتحقق مما إذا كان هذا token
صالحًا، فنحن نكتب واجهة بعد تسجيل الدخول:
app.get("/afterlogin"، (req, res) => { const {headers } = req; رمز const = headers["authorization"].split(" ")[1]; // ضع الرمز المميز في حقل التفويض بالرأس jwt.verify(token, jwtKey, (err, payload) => { إذا (أخطأ) قم بإرجاع res.sendStatus(403); res.json({ message: "تم المصادقة بنجاح"، payload }); }); });
في هذا الكود، يتم الحصول على token
الذي تم إنشاؤه مسبقًا من خلال JWT عن طريق الحصول على token
في حقل authorization
في رأس الطلب. ثم تحقق مما إذا كان token
صالحًا عن طريق استدعاء طريقة التحقق jwt.verify
. تحتوي هذه الطريقة على ثلاث معلمات:
// هناك أربعة توقيعات للواجهة، يمكنك التحقق من الوثائق بنفسك، وظيفة التصدير تحقق ( رمز: سلسلة، // الرمز الذي يحتاج إلى التحقق SecretOrPublicKey: سر | GetPublicKeyOrSecret، // مفتاح التوقيع المحدد في رد اتصال الخادم؟: VerifyCallback<JwtPayload string>, // رد الاتصال للحصول على نتيجة معلومات التحقق): void;
بعد ذلك، ننسخ token
الذي استجبنا له للتو في رأس الطلب:
### احصل على http://localhost:3000/afterlogin التفويض: حامل eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjUyNzg5NzA3LCJleHAiOjE2NTI3ODk3Mzd9.s9fk3YLhxTUcpUgCfIK 4xQ N58Hk_XEP5y9GM9A8jBbY
مصادقة Bearer السابقة هي طريقة المصادقة القياسية في بروتوكول http.
كذلك اضغط على Send Request
وعندما ترى الرد في الصورة أدناه فهذا يعني أن الرد ناجح:
في الواقع، ما ورد أعلاه هو بعض الاستخدامات البسيطة لـ JWT. بعد ذلك، دعونا نتحدث عن مزايا وعيوب JWT نفسها.
مساحة التخزين التي تشغلها JWT ليست صغيرة في الواقع. إذا أردنا التوقيع على الكثير من المعلومات، فمن المحتمل أن يتجاوز الرمز الحد الأقصى لطول ملف تعريف الارتباط. على سبيل المثال، قارن بين هاتين الصورتين:
من الواضح أنه مع زيادة كمية المعلومات في الحمولة، سيزداد طول الرمز المميز أيضًا؛
الأمان، في الواقع، إذا كان token
يشغل مساحة كبيرة جدًا، فإن الحد الأقصى لمساحة تخزين Cookie
هو 4 كيلو بايت فقط. يمكن تخزين الواجهة الأمامية في وحدة تخزين محلية مثل localStorage
، ولكنها ستسبب مشكلة إذا لم يتم وضعها في ملف تعريف الارتباط، سيتم تقليل الأمان بشكل كبير، وسيكون هناك خطر الحصول عليه من خلال البرنامج النصي js، مما يعني أن أي متسلل يمكنه فعل أي شيء به؛
في الواقع، هناك جانب معين من JWT هو أن token
المستخدم لا يحتاج إلى تخزينه بشكل مستمر، وبدلاً من ذلك، يتم التحقق token
بشكل فعال باستخدام التحقق من الخادم إذا تم تغيير وقت انتهاء الصلاحية، فسيتم التلاعب token
نظرًا لعدم وجود طريقة لتخزين تاريخ انتهاء الصلاحية وتغييره يدويًا، فمن الصعب حذف token
على الفور إذا قام المستخدم بتسجيل الدخول مرتين وتم إنشاء token
مميزين، ثم قم بالدخول من حيث المبدأ، سيتم إنشاء token
صالحين.
ما سبق يتحدث بشكل رئيسي عن عدة نقاط:
مبدأ JWT هو بشكل أساسي استخدام المفتاح الخاص للخادم للتواصل مع token
الناتج عن توقيع JSON؛
كما يقدم أيضًا تكوين البيانات الداخلية لـ JWT، والتي يستخدمها Header لتحديد خوارزمية التوقيع ونوعه، والحمولة النافعة لنقل بيانات JSON، والتوقيع لتنفيذ خوارزمية التوقيع على البيانات ومنع التلاعب بها؛
يتم تقديم مقدمة تفصيلية حول كيفية استخدام JWT من خلال Nodejs، وتنفيذ توقيع البيانات من خلال طريقة sign
، وتنفيذ التحقق من التوقيع من خلال طريقة verify
؛
تم أيضًا تقديم بعض عيوب JWT:
الأول هو أن مساحة التخزين تزداد مع زيادة كمية بيانات التوقيع؛
ثم هناك الأمان، إذا كانت مساحة التخزين كبيرة جدًا، فلن يتم تخزينها في Cookie
بمستوى أمان مرتفع نسبيًا، مما يؤدي إلى الحصول على البرنامج النصي حسب الرغبة؛
ثم هناك التوقيت المناسب، الذي لا يمكنه التحكم بمرونة في توقيت token
؛
هذا هو الكود المصدري التجريبي لـnodejs أعلاه كمرجع؛