تأمين واجهة ASP.NET Core Web API باستخدام Key Authentication

-

تأمين Web APIs  بواسطة KEYS 

طبعا وبالتأكيد وحسب طبيعة API بتكون إمكانية الوصول، بعض API يجب حمايتها بحيث لا نريد السماح للجميع بالوصول اليها. بناء عليه اكيد نحنا بحاجة الى تأمين Web APIs باستخدام Keys. وبالتالي أي عميل يريد استخدام هذا API المحمي بتحتم عليه  إرسال API Keys مع الطلب ، 

تمام لحد الان الأمور واضحه، 

طيب بعد ما العميل ارسل الطلب مع  API Keys شو بصير بعدين:

الان دور API حيث يقوم بفحص API Keys  وإذا كانت صحيحة ، فسيتم إرسال الاستجابة اذا لا برجع خطأ.

Best practice في هذا الموضوع هو ارسال API Keys هي من خلال Http Request Header.


تمام نكمل بمثال عملي حتي نفهم هذا الموضوع بشكل افضل.

خلونا نرجع للمشروع الخاص ب Web API الي كان اسمو MyFirstAPIProject ونعمل كم تعديل. الهدف من هذا التعديل هو السماح للعملاء المعتمدين فقط من الدخول والاستعراض او الاضافة. 

طيب انتقل الان الى controller الى كان اسمها CourseController ونروح بعدها الى action  الى اسمو GetCoursesModel  وبعدها نعدل على الكود السابق الى 

// GET: api/Course
        [HttpGet]
        [Route("GetAllCourses")]
        public async Task<ActionResult<IEnumerable<CoursesModel>>> GetCoursesModel()
        {
           
            if (!Authenticate())
                return Unauthorized();
            connection = new SqlConnection(configuration.GetConnectionString("STDCS"));
            List<CoursesModel> CoursesModelList = new List<CoursesModel>();
            try
            {
               string sqlQuery ="select CoursesId, CourseNumber, CourseName, CourseDescription, Price, Capacity, CreatedDate  from courses";
                DbProviderFactory dbFactory = DbProviderFactories.GetFactory(connection);
                using (var cmd = dbFactory.CreateCommand())
                {
                    cmd.Connection = connection;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = sqlQuery;
                    using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
                    {
                        adapter.SelectCommand = cmd;
                        await Task.Run(() => adapter.Fill(Dt));
                    }
                }
foreach (DataRow BDM in Dt.Rows)
                {
                    CoursesModelList.Add(new CoursesModel
                    {
                        CoursesId = Convert.ToInt16(BDM["CoursesId"]),
                        CourseNumber = BDM["CourseNumber"].ToString(),
                        CourseName = BDM["CourseName"].ToString(),
                        CourseDescription = Convert.ToString(BDM["CourseDescription"]),
                        Price = Convert.ToDecimal(BDM["Price"]),
                        Capacity = Convert.ToInt32(BDM["Capacity"]),
                    });
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
             }
            return (CoursesModelList);
           
            //return await _context.CoursesModel.ToListAsync();
        }

الكود الجديد هو 

            if (!Authenticate())
                return Unauthorized();

Authenticate() هو function  بنضيفو في controller الهدف  منو هو التأكد وفحص Key المبعوث الى API والكود الخاص فيو هو :
bool Authenticate()
        {
            var allowedKeys = new[] { "abc@123", "abc225@@", "AbcABC@@" };
            StringValues key = Request.Headers["Key"];
            int count = (from t in allowedKeys where t == key select t).Count();
            return count == 0 ? false : true;
}

اكيد ما تنسي تضيف using Microsoft.Extensions.Primitives;  لحتي يتعرف على StringValues
بهذا ال function يوجد 3 Keys مسموح بها تم اضافتهم الى المتغير allowedKeys.بعدها يتم التحقق من المفتاح المبعوث في Header اذا كان يتطابق مع أحد هذه المفاتيح الثلاثة،  اذا نعم بتم التنفيذ ، اذا لا برجع خطأ .
لحد الان الأمور تمام.
بهيك بنكون جهزنا ال Web API بمفتاح حماية . 
طيب كيف ممكن نستخدم هذا المفتاح عند استدعاء API  وكيف بنرسل هذا Key في Header  خلونا نشوف كيف مع بعض: 

ننتقل الان الى المشروع Student Portal، وبعدها نروح الى CourseController وبعدها نعمل التعديل التالي على Index 
public async Task<IActionResult> Index()
        {
            List<CoursesModel> CoursesList = new List<CoursesModel>();
            using (var httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Add("Key", "abc123@123");
                using (var response = await httpClient.GetAsync("https://localhost:7081/api/Course/GetAllCourses"))
                {
                    string apiResponse = await response.Content.ReadAsStringAsync();
                    if (response.StatusCode == System.Net.HttpStatusCode.OK)
                    {
                        
                        CoursesList = JsonConvert.DeserializeObject<List<CoursesModel>>(apiResponse);
                    }
                    else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        ViewBag.Result = apiResponse;
                        //return View(CoursesList);
                        return Unauthorized();
                    }
                }
            }
            return View(CoursesList);
        }

لاحظ في الكود
 httpClient.DefaultRequestHeaders.Add("Key", "abc123@123");

ارسلنا Key في Header مع قيمة هذا Key، وبعدها في الكود فحصنا النتيجة اذا كانت 
if (response.StatusCode == System.Net.HttpStatusCode.OK)
بكون Key صحيح  وبرجع النتائج المطلوبة 

اذا كانت النتيجة 
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
بكون Key غير صحيح

هنا في هذا المثال ارسلنا ال Key مختلف لذا بتكون النتيجة 401 status code
 
وشكل الشاشة :