PracticU

הסבר על JWT לאימות משתמשים

בלי כתוביותעם כתוביות
PracticU › קורס NodeJS › הסבר על JWT לאימות משתמשים
מילון מושגים:
JWT - אסימון חתום בפורמט JSON שמעביר נתוני זהות והרשאה בין צדדים באופן קומפקט ומאומת.
טוקן - פיסת מידע שמשמשת לאימות או להרשאה, שהמערכת מנפיקה ללקוח ומוצגת בבקשות הבאות במקום סיסמה.
עוגייה - קובץ קטן שאתר שומר בדפדפן כדי לזכור מידע על המשתמש בין בקשות, למשל מצב התחברות או העדפות.
כותרת HTTP - שדה מידע נלווה בבקשה או בתגובה של HTTP, המעביר נתונים כמו סוג התוכן, אימות או הגדרות מטמון.
Middleware - שכבת תוכנה שמתווכת בין רכיבים ומטפלת בבקשות בדרכן, למשל לאימות, רישום לוג או עיבוד נתונים.
Callback - פונקציה שמועברת כארגומנט לפונקציה אחרת כדי שתופעל מאוחר יותר, לרוב בסיום פעולה.
jQuery - ספריית JavaScript ותיקה שמפשטת את הגישה לרכיבי הדף, אירועים ואנימציות בדפדפן.
AJAX - טכניקה שמאפשרת לדף אינטרנט לשלוח ולקבל נתונים מהשרת ברקע ולעדכן את התצוגה בלי לטעון מחדש.

הסבר על JWT לאימות משתמשים
בהרצאה הקודמת הגנו על ראוטים באמצעות Session. כאן עוברים לטכניקה שאינה שומרת דבר בשרת ונפוצה הרבה יותר היום: JSON Web Token, או JWT.
למה לא Session
ה-Session שמור באפליקציה אחת בשרת אחד. אם המשתמש גולש לאפליקציה אחרת, למיקרוסרוויס אחר או לשרת אחר, ה-Session לא יהיה שם. אפשר לשמור אותו בשרת מרכזי, אבל המרצה מגדיר זאת כמסובך מדי. ובשירותי REST גם ככה לא נהוג להשתמש ב-Session.
איך JWT עובד
1. יצירה בשרת: אחרי register או login השרת מחזיר ללקוח בתגובה מחרוזת ארוכה. אפשר לשלב בתוכה אובייקט, למשל ה-user, וגם זמן תפוגה.
2. השרת לא שומר אותה: הלקוח שומר את המחרוזת ב-cookie, ב-Session Storage או ב-Local Storage.
3. שליחה בכל בקשה: לראוט שדורש login הלקוח שולח header בפורמט authorization: Bearer <token>, בלי גרשיים.
4. בדיקה בשרת: השרת שולף את ה-header, מחלץ את הטוקן ובודק שהוא חוקי ושלא פג תוקפו. אם יש בעיה הוא מחזיר 401 או 403, ואם לא, נותן את השירות.
מימוש בשרת
1. התקנה: npm i של jsonwebtoken, ו-require של הספרייה בקונטרולר.
2. יצירת טוקן: jwt.sign מקבלת payload (כאן אובייקט ה-user), מפתח סודי ו-options. המפתח הסודי, kittens are cute, הוצא לקובץ קונפיגורציה תחת config.jwt.secretKey, כי משתמשים בו ביותר ממקום אחד.
3. תפוגה: ב-options מגדירים expiresIn של 30 דקות. כך טוקן שנגנב לא יעבוד לנצח.
4. החזרה ללקוח: גם ב-register וגם ב-login מחזירים בתגובה את ה-user ואת הטוקן.
5. Middleware בשם verifyLoggedIn: אם אין request.headers.authorization מחזירים 401 עם "you are not logged in". אחרת עושים split לפי רווח ולוקחים את פריט 1 במערך. אם אין טוקן, מחזירים שוב 401.
6. אימות: jwt.verify מקבלת את הטוקן ואת המפתח הסודי, ופונקציית callback עם error ו-payload. אם יש שגיאה מחזירים 403, ואם לא, קוראים ל-next.
7. הפעלה: ב-products-controller מוסיפים router.use(verifyLoggedIn).
בצד הלקוח
1. אחרי register או login שומרים ב-Session Storage גם את response.token, בלי JSON.stringify כי זו מחרוזת.
2. ב-jQuery משתמשים ב-$.ajaxSetup כדי לצרף את ה-header לכל בקשת Ajax. אם המשתמש מחובר, ה-header הוא Bearer ורווח ואז הטוקן.
3. Logout מנקה רק את ה-storage אצל הלקוח. השרת לא עוסק ב-logout.
בדיקות ושגיאות
אחרי logout הגישה ל-api/products מחזירה "you are not logged in". גם טוקן שהומצא מחזיר שגיאה. הדפסת השגיאה הראתה JWT malformed. בסוף ההרצאה הוסיף המרצה הבחנה לפי err.message: אם ההודעה מציינת expired, מחזירים 403 "your login session has expired", ואחרת 401 "you are not logged in".
אחרי ההרצאה אפשר להגן על ראוטים בעזרת JWT בלי לשמור Session בשרת.
השיעור הבא בקורס: הגנה מהתקפות SQL Injection

פרקי ההרצאה: הסבר על JWT לאימות משתמשים

  1. 00:00

    הבעיה של Session בכמה שרתים

  2. 01:20

    האתר לא מוגן בלי Session

  3. 02:19

    איך עובד JSON Web Token

  4. 04:02

    שליחת הטוקן ב-Authorization Header

  5. 06:26

    התקנת jsonwebtoken ויצירת טוקן

  6. 09:06

    העברת ה-secret לקובץ קונפיגורציה

  7. 10:39

    הגדרת expiresIn והחזרת הטוקן

  8. 12:09

    middleware לאימות הטוקן בשרת

תמלול ההרצאה: הסבר על JWT לאימות משתמשים

הטקסט המלא של הדקות הראשונות בהרצאה «הסבר על JWT לאימות משתמשים» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.

  1. 00:03

    אנחנו רוצים לבצע בדיוק את אותו הדבר שביצענו בסרטון הקודם ולהגן על הסרבר מפני כניסה לכל מיני ראוטס שדורשים לוגין, מבלי שהקליינט יבצע לוגין, אבל לבצע את זה בטכניקה אחרת שהיא הרבה יותר פופולרית היום.

  2. 00:21

    קודם כל, מה בדיוק הבעיה עם שימוש בטכניקה הקודמת, בפשן? סשן הוא פר מחשב, לכן אם לדוגמה אנחנו מריצים את הפרויקט שלנו בסרבר במייקרו סרוויס אזולאי או שהוא נמצא ב... יש מספר אפליקציות שתומכות בו.

  3. 00:41

    אז סשן נמצא באפליקציה אחת ונותנת שירות רק מאותה אפליקציה שגלשו אליה. אם היוזר בגלישה הבאה גולש לאפליקציה אחרת, מיקרו סרוויס אחר, מחשב אחר, שרת אחר בשביל לקבל את השירות, לא יהיה לו שם בדיוק את הפשן.

  4. 00:58

    יש פתרונות לדבר הזה בצד השרת, לשים את ה-session באיזשהו שרת מרכזי ורק ממנו לקחת את זה אבל זה too complicated אז בואו נראה טכניקה אחרת שלא משתמשת בכלל ב-session ב-server side כאשר אנחנו משתמשים ב-Restful Service ואני מזכיר לכם שב-Restful Service גם הרבה פעמים לא נהוג בכלל להשתמש ב-session אז לצורך כך לקחתי את אותה דוגמה והחזרתי

  5. 01:27

    הכל אחורה כל מה שהיה בדוגמה הקודמת הורדתי אין לנו אפילו את ההתקנה של ה-Express Session בפרויקט הזה, אין לנו תמיכה ב-Session וזה אומר שאם הלקוח גולש אלינו, בואו רגע נריץ, Node App, Go, אם הלקוח גולש אלינו מבלי לבצע לוגאין הוא כן יצליח לקבל את הערכים. בואו נגלוש ל-API Product ונראה שהוא הצליח לקבל את כל המידע,

  6. 01:59

    זה בעצם ה-Security Hack שראינו. בואו נלך ל-F12, נראה שגם אם אנחנו הולכים ויוצרים פה סתם איזה User ב-Session Storage, אפילו בואו ננסה User בצורה כזאת, ננסה ללכת ל-Products, והנה אנחנו רואים את המוצרים.

  7. 02:23

    אז האתר כרגע לא מוגן, אוקיי? נמחק את זה רק. האתר כרגע לא מוגן, אנחנו רוצים לגשת ל-Server Side ולהגן עליו.

  8. 02:32

    שיטה שאנחנו הולכים לראות נקראת JSON Web Token JWT. JSON Web Token אומרת את הדבר הבא יש לנו קליינט והקליינט מבצע גלישה לסרבר והסרבר בלוגאין מה שהקליינט עושה אם הקליינט מבצע לוגאין או רג'יסטר גולש לסרבר הסרבר מחזיר לקליינט ברספונס משהו שנקרא json-web-token זאת איזושהי

  9. 03:11

    מכרוזת ארוכה שמכילה המון המון מידע היא די ארוכה והמידע הזה שהיא מכילה אומר שא', יש פה איזושהי חוקיות בנוגע למכרוזת הזאת, ב', שאני יכול לשלב בתוך המכרוזת איזה אובייקט שאני רוצה לדוגמה את האובייקט של היוזר, אימייל יש אקספיריישן טיים שאנחנו יכולים לשלב בתוך המחרוזת הזאת.

  10. 03:42

    אנחנו מייצרים אותה בצד של הסרבר ומחזירים אותה לקליינט, אבל אנחנו לא שומרים אותה בסרבר בשום מקום. עכשיו היא נמצאת בצד של הקליינט, הקליינט לוקח אותה ושומר אותה בקוקי, בפשן סטורג', בלוקל סטורג', איפה שהוא רוצה.

  11. 03:59

    הסרבר אין לו יותר את המחרוזת הזאת והוא לא זוכר אותה בכלל. הקליינט רוצה להיכנס לדף המוצרים, פרודקטס. הוא חייב להביא לנו ברקווסט את המחרוזת הזאת.

  12. 04:13

    ולא סתם ברקווסט, יש מוסכמה בדיוק איפה המחרוזת הזאת צריכה להיות. המחרוזת הזאת צריכה להיות באוסף ה-Headers של היוזר ולהופיע בפורמט הבא authorization.2 ובעצם הפורמט שהיא צריכה להיות.

  13. 04:35

    אחר כך אנחנו רושמים אחרי הנקודותיים רווח ואז ברר רווח ואז המחרוזת בלי גרשיים. זאת מחרוזת הטוקן שהשרת יצר והחזיר לקליינט ושהמחרוזת הזאת מכילה מידע ספציפי שהשרת יוודא שהוא נכון.

  14. 04:56

    זה מפה עד לפה זה הדר שהקליינט חייב לשלוח כל פעם שהוא רוצה לגלוש לאיזשהו ראוט שדורש לוג די אין או משהו כזה והסרבר עכשיו כל פעם שזה קורה מקבל מחדש את ההדר של היוזר, שולף את ההדר הזה, ההדר הזה בעצם זה זה, מחפש הדר אוטוריזיישן, שולף אותו וצריך לחלץ מהמחרוזת הזאת רק את הטוקן שזה זה,

  15. 05:31

    כלומר הוא מחפש ברר רווח ומעיף, מקבל רק את הטוקן אז הוא בודק, א', האם הנוסחה היא חוקית, ב', האם הטיים עדיין לא אקספיירד, אולי נתתי לו הזדמנות להיות רק חצי שעה בשרת והוא מנסה אחרי שעה לגלוש אליי.

  16. 05:49

    כל הדברים האלה הסרבר בודק. אם יש איזושהי בעיה, הסרבר מחזיר, או 401 או 403, אנחנו תכף נראה. אם אין בעיה, אז הוא נותן לו את השירות שהקליינט מחפש.

  17. 06:01

    אז הטריק פה זה שהסרבר מייצר את המחרוזת המיוחדת הזאת, הוא שולח אותה לקליינט, הוא לא שומר אותה בשום מקום, הרי יש פה ריספונס לקליינט, והקליינט מחויב לשלוח אותה כל הזמן בסרבר.

  18. 06:14

    זאת הטכניקה של JWT. אנחנו הולכים לממש את זה כמובן גם בסרבר, גם בקליינט. דבר ראשון, אנחנו צריכים להתקין את הספרייה שמאפשרת לנו לעבוד עם JWT.

  19. 06:26

    אז בואו נבצע NPMI ואנחנו צריכים להתקין JSON Web Token. נתקין את זה, נראה שהכל מותקן כמו שצריך, יופי. וכעת אנחנו רוצים לשלב את זה.

  20. 06:47

    אז כאמור מחקתי את כל מה שהיה בגרסה הקודמת עם ה-Session ואין לי פה שום אזכור ל-Session. הדבר הראשון אנחנו הולכים לקונטרולר, אנחנו עושים דברים מאוד דומים למה שעשינו בסרטון הקודם אבל כמובן אנחנו עובדים כרגע עם JWT.

  21. 07:01

    בואו נראה רג'יסטר אם היוזר הוא רג'יסטר ואני רואה שהוא הצליח להירשם בהצלחה אני בא לפה ואנחנו לפני שנעשה את זה כמובן אנחנו צריכים פה const את נקרא לזה jwt require json web token אז אנחנו באים לכאן ואנחנו יוצרים טוקן חדש.

  22. 07:31

    מתי אנחנו יוצרים טוקן חדש או שהוא עשה רג'יסטר או שהוא עשה לוגים? טוקן החדש נוצר על ידי הפקודה jwt.sign ואנחנו כאן נותנים לו משהו שנקרא payload.

  23. 07:45

    payload זה איזה מכרוזת, מספר, אובייקט שאנחנו רוצים שהוא יגלם וישתול בתוך ה-JSON Web טוקן שהוא מחזיר לקליינט.

  24. 07:57

    אולי כאשר היוזר גולש עכשיו למוצרים, אנחנו מוציאים בסרבר סייד את המכרוזת הזאת, כן, זה הסרבר, מוציאים ושולפים את המכרוזת מההדר, אולי אנחנו רוצים לדעת מי זה היוזר, אולי אנחנו רוצים לדעת מה התפקיד שלו, אולי רק אם הוא אדמין, אנחנו רוצים לתת לו את השירות הזה לכן אנחנו יכולים לשלב בתוך המחוזת איזה אובייקט שאנחנו רוצים אז זה מתבצע בצורה כזאת

  25. 08:23

    ואנחנו נותנים את אובייקט יוזר כי היוזר הוא זה שכרגע עשה לנו רג'יסטריישן אז אנחנו נותנים לו את האובייקט יוזר ולאחר מכן המערכת אומרת לנו תנו לנו פה איזשהו סיקרט קי שרק אתם יודעים בשביל שדרכו נצפין את כל ה-JSON Web טוקן כי הדבר הזה הולך לחזור לקליינט אנחנו לא רוצים שהקליינט ידע פורמטים ידועים של ה-JSON Web Token הזה ויעשה איתם כל מיני

  26. 08:52

    דברים או שיפענח וישנה לנו את הפורמט הזה. אז אנחנו צריכים לייצר איזושהי מכרוזת, כמו המכרוזת שהייתה לנו ב-Session שערבלה את ה-Session ID.

  27. 09:06

    המכרוזת הייתה, אם אני לא טועה Kittens RQ, כמובן לא משנה כל כך מה המכרוזת, הוא משמש איתה בשביל סוג של סולט או האשינג או ערבול כזה של אותה מחרוזת שהוא מחזיר לקליינט, אבל אנחנו רוצים להשתמש איתה בעוד מקומות.

  28. 09:24

    לכן בואו נוציא אותה לקובץ קונפיגורציה. כאשר עשינו את המחרוזת הזאת עם סשן, נענו שימוש בה רק במקום אחד. בגלל שאני רוצה להשתמש איתה גם פה נניח וגם פה, אז בואו נוציא אותה לקובץ קונפיגורציה בשביל שהיא תהיה אחת ויחידה רק במקום אחד.

  29. 09:40

    אז בואו נלך לקובץ הזה, האמת שאנחנו צריכים לעשות את זה בשני הקבצים ונייצר כאן בקובץ הקונפיגורציה JWT וניתן אובייקט וניתן לו secret ואז ניתן את המחוזת שהייתה זאת המחוזת kittens are cute את הדבר הזה אנחנו צריכים גם בפרודקשן כי זה לא משנה לנו אם זה development או פרודקשן זה אותו הדבר ויש לנו כרגע אפשרות לגשת ל-config, פה אנחנו רוצים config.jwt ו-secret key וזה

  30. 10:35

    יביא לנו בדיוק את המחרוזת הזאת. האובייקט השלישי זה אובייקט של options שבו אנחנו יכולים לתת, אנחנו לא חייבים אבל זה מקובל מאוד לתת expires in, שזה מחרוזת שאומרת בעוד כמה זמן אנחנו רוצים שהטוקן הזה כבר לא יהיה רלוונטי.

  31. 10:57

    אקספיירס, פג תוקף. היוזר לא יכול עכשיו לגלוש, לקבל טוקן ובעוד שלושה ימים לגלוש עם הטוקן הזה. כי מישהו יכול לגנוב לו את הטוקן ואז לגלוש תמיד לאתר שלנו.

  32. 11:07

    אז אנחנו יכולים לתת איזשהו מספר בשניות, בדקות, בשעות, בואו ניתן 30 minutes, 30 דקות בצורה כזאת.

  33. 11:23

    הדבר הבא שאנחנו רוצים לעשות זה להחזיר ברספונס גם את היוזר וגם את הטוקן בצורה כזאת. חזרנו לו גם את היוזר שלו וגם את המחוזת, זה מה שאנחנו מחויבים לעשות לו.

  34. 11:39

    בואו ניקח את אותו הדבר ניגש מיד אחרי שהיוזר ביצע לוג-אין בהצלחה זה יהיה פה. נייצר שוב פעם טוקן חדש עבור היוזר כי הרי הוא עשה לנו לוג-אין ונחזיר לו את הטוקן חוזרת בחזרה לקליינט בצורה כזאת.

  35. 11:59

    אז עכשיו סיימנו את התהליך של רג'יסטר ולוגין, יצרנו טוקן, החזרנו לקליינט. גם כאן החזרנו לקליינט.

  36. 12:09

    הדבר הבא שאנחנו רוצים זה להגן על ראוטס שאנחנו לא מאשרים להיכנס אליהם בלי ביצוע לוגדין. זה כמובן היה המוצרים בדוגמה שלנו.

  37. 12:27

    אז בסרטון הקודם הגענו על זה על ידי Middleware שבודקת האם היוזר לוג-אין וזאת הייתה שיטה טובה. בואו נעשה בדיוק את השיטה הזאת, אבל כמובן נבדוק עכשיו את ה-JWT.

  38. 12:39

    אז אנחנו רוצים להוסיף את הספרייה Middleware ואנחנו רוצים בתוכה להוסיף בדיוק את אותו הכובד שהיה לנו VerifyLoggedIn.js.

  39. 12:54

    הנה יש לנו פה פונקציה חדשה שאנחנו הולכים לבנות. אנחנו צריכים את ה-JWT, Require JSON Web Token. אנחנו רוצים לבנות את אותה פונקציה, ו-Refi Logged In, מקבלת request, response ו-next.

  40. 13:16

    זה middleware שבודק אם ה-user לוגדים. כעת אנחנו צריכים לבדוק מספר דברים. הרי מתי אנחנו הולכים להשתמש עם הפונקציה הזאת? אנחנו הולכים להשתמש איתה ב-controller שמקבל request, אבל שהוא חייב להיות לוקדין בשביל הריקווסט הזה.

  41. 13:34

    זאת אומרת שאם הוא שולח, הוא רוצה לגלוש לריקווסט הזה, לראוט הזה, הוא היה חייב לשלוח לי את ההדר. זה מה שהאחריות של היוזר לבצע. אם היוזר לא יעשה את זה, אני לא נותן לו שירות.

  42. 13:51

    אוקיי? זה כבר יש פה חלק מהאחריות של המערכת, קשורה גם ליוזר, לא רק לקליינט. אז הכוונה, לא רק לסרבר, הכוונה, האחריות צריכה להיות של ה-Client Application לא רק של ה-Server כמו שהיה ביישום של ה-Session.

  43. 14:08

    אז כאן ה-Client Application אמור לשלוח אלינו את ה-Header הזה שאנחנו יודעים איך הוא קיים. בואו רגע נסתכל עוד פעם מה הפורמט של ה-Header.

  44. 14:21

    זה היה הפורמט של ה-Header. נניח שפה יושב לנו, נקרא לזה My Token, וה-Token נמצא אחרי הברר, אחרי רווח אחד ב-Header עכשיו אנחנו צריכים לבדוק סדרת דברים.

  45. 14:37

    קודם כל בואו נבדוק אם בכלל אין לו header כזה. לא הביא לי משום סיבה header כזה, מאיזושהי סיבה. אם not request.headers.autorization וואלה,

לקורס המלא: קורס NodeJS אונליין בעברית

ההרצאה הקודמת: הסבר על סשן Session לאימות משתמשים

ההרצאה הבאה: הגנה מהתקפות SQL Injection

הרצאות קשורות מקורסים אחרים

  • משתמשים מקומיים - חלק 1
  • יצירת משתמשים ב-AD
  • שליחת JWT Token - ריאקט
  • ניהול משתמשים בענן

קורסים נוספים במסלול

  • קורס יסודות HTML - בניית אתר אינטרנט פשוט
  • קורס CSS 3 - בדגש על Flex & Grid
  • קורס Javascript כולל JQuery
  • קורס ג'אווה סקריפט מעשי - JavaScript
  • קורס TypeScript
  • קורס ריאקט - React