26/05/2019

אימות משתמש – צד שרת

שיתוף ב facebook
שיתוף ב twitter
שיתוף ב linkedin

הפעם נדבר על פרוייקט שבנינו לאחד הלקוחות שלנו. ספציפית, נכתוב על איך החלטנו להטמיע אימות משתמש (Login) וננסה להסביר למה.

איך מתבצע תהליך אימות?

בפרוייקט הנ"ל בחרנו ב- JWT כטוקן לאימות המשתמשים ולכן נתמקד בזה, למרות שהתהליך אינו שונה באופן מהותי בין אם בחרתם ב-JWT או Cookie.

אז תהליך האימות, ללא קשר לטכנולוגיית לקוח ו/או שרת צריך להתבצע בצורה הבאה:

1. הלקוח מבקש לבצע אימות (ע"י שליחת שם משתמש וסיסמא, רצוי ב- Request Body).

2. השרת מאמת את הנתונים (בדרך כלל נשמור את פרטי המשתמש והסיסמא שלו בבסיס הנתונים – הסיסמא לעולם לא נשמרת כ- Clear Text בבסיס הנתונים) – תכלס, כל עניין שמירת סיסמאות ושימוש ב- Salt שווה פוסט נפרד – Stay Tuned!  במידה והאימות בוצע בהצלחה, השרת מייצר JWT ומחזיר אותו בגוף התגובה ללקוח. במידה ולא, יש צורך להחזיר הודעת שגיאה מתאימה.

3. הלקוח שומר את פרטי ה- JWT אצלו. מכאן ואילך נוסיף את ה- JWT לכל קריאה שתשלח לשרת. איפה נשמור את פרטי ה- JWT? איך נוסיף את ה- JWT לכל קריאה שהלקוח מבצע לשרת? תאלצו להמשיך ולקרוא את הפוסט…

אז איך מאמתים משתמש?​

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

ברמת השרת זה מאוד פשוט (לפחות ב- .NET) – מוסיפים Authorization Attributes לכל מתודה! השרת יודע "לפרק" מהבקשה המתקבלת את מחרוזת ה- JWT ולזהות האם הלקוח רשאי לקרוא למתודה הנ"ל. במידה וכן, השרת ממשיך לקוד המתודה, ובמידה ולא – מחזיר הודעת שגיאה (בדרך כלל Authorization Error).

קצת Best Practice: בכל ספרייה מומלץ להגדיר את כולה כ- Authorize, ז"א ספרייה הדורשת אימות לפני קריאה לכל מתודה המוגדרת בה, ועבור מתודות ספציפיות שנרצה להגדיר כפתוחות לכולם (כמו למשל מתודת Login), נגדיר רק אותם כ- AllowAnonymous.

כדי לאפשר אימות בצד השרת, יש להוסיף את הקוד app.UseAuthentication למתודת Configure, בקובץ Startup.cs.

כדי לאפשר שימוש ב-JWT בצד השרת, יש להוסיף את הקוד הנ"ל למתודת ConfigureServices באותו הקובץ:

var appSettings = appSettingsSection.Get();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
    x.RequireHttpsMetadata = false;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = false,
        ValidateAudience = false,
        ValidateLifetime = true,
        ClockSkew = System.TimeSpan.Zero
    };
});

הקוד המגדיר את המשתנה key, דורש הסבר קצר… בעצם key הוא מערך של Bytes ממחרוזת שנגדיר איפשהו. מה זה איפשהו? בסביבת פיתוח אפשר להגדירה בקובץ JSON חיצוני. אנו קוראים מהקובץ את החלק הרלוונטי (appSettings.Secret) ליצירת המערך (גם לזה נפרסם פוסט, אל דאגה). 

בסביבת Production נגדיר את המחרוזת בדרך כלל כמשתנה מערכת (משהו כמו export key = my_long_secured_key)

אמרנו שהשרת מחזיר מחרוזת JWT ללקוח במידה והאימות נעשה בבהצלחה. אם כך, מתודת ה- Login שלנו חייבת לכלול יצירת מחרוזת JWT. במחרוזת זו אנו יכולים לקודד פרטים שונים כמו – שם משתמש, מס' משתמש, תפקיד (Role). יש להוסיף פרטים בזהירות רבה מאחר ואפשר למצוא את הפרטים הללו דיי בקלות מתוך מחרוזת הזו. בנוסף לפרטים שבחרנו להוסיף למחרוזת, תמיד נוסיף את תאריך התפוגה שלה (כן, נגדיר למחרוזת JWT תפוגה).

איך הקוד נראה? הינה דוגמא:

1  var tokenHandler = new JwtSecurityTokenHandler();
2  var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
3  var tokenDescriptor = new SecurityTokenDescriptor
4  {
5      Subject = new ClaimsIdentity(new Claim[] {
6      new Claim("username", user.username, ClaimValueTypes.String),
7      new Claim("user_role", user.role, ClaimValueTypes.String),
8      new Claim("user_id", user.id.ToString(), ClaimValueTypes.Integer32)
9  }),
10    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
11 };
12 tokenDescriptor.Expires = DateTime.UtcNow.AddDays(1);
13
14 var token = tokenHandler.CreateToken(tokenDescriptor);
15 var tokenString = tokenHandler.WriteToken(token);
16 return (tokenString);

קצת הסבר לקוד:

שורות 3-9 מגדירות את הפרטים שנרצה להחזיר ללקוח בתוך מחרוזת ה- JWT

שורה 12 מגדירה את זמן תפוגת המחרוזת.

שורת 14, 15 מייצרות את המחרוזת עפ"י הנתונים שהגדרנו.

בפוסט הבא נדבר על צד הלקוח – נסביר באיזה חבילות בחרנו להשתמש (רמז – אנגולר) הדילמות שהיו לנו ואיך בחרנו להטמיע.

זה הזמן לאונליין!

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

בניית אתר תדמית, תוכן,  חנות וירטואלית או הנגשת מערכת ה-"בקאנד" של העסק לתמוך בעבודה מהבית ולהשאיר את העסק חי.

עכשיו זה הזמן! צרו קשר איתנו עוד היום ונתחיל.

 

זה הזמן לאונליין!

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

בניית אתר תדמית, תוכן,  חנות וירטואלית או הנגשת מערכת ה-"בקאנד" של העסק לתמוך בעבודה מהבית ולהשאיר את העסק חי.

עכשיו זה הזמן! צרו קשר איתנו עוד היום ונתחיל.