تأمين واجهة swagger في production في ASP.Net Core net5.0 - Securing swagger UI in production in ASP.Net Core net5.0
تطوير الويب Backend - ASP.NET Core API
بنحكي في هذه المقال عن تأمين وحماية واجهة API المطورة باستخدام swagger، الهدف إضافة اسم مستخدم وكلمة مرور للسماح بالوصول الى الصفحه وحماية الروابط الموجودة فيها. الهدف منع المستخدمين من الوصول الى الصفحه بعد عمل release ونشر API.

بنستخدم في هذه الطريقة أليات المصادقة authentications mechanism لحماية swagger API.
تمام نفرض انو عندك مشروع API جاهز وانت فقط بحاجه الى تأمين API.
الفكرة:
بنعمل class لإضافة authentications وفي هذا المثال ثبتنا الكود الخاص باسم المستخدم وكلمة المرور(اكيد ممكن تعمل تطوير للطريقة وقراءة هذه البيانات من قاعدة البيانات مثلا). لكن الان في هذه المرحلة ثبتنا هذه البيانات في الكود.
أولا نضيف مجلد باسم Helpers في المشروع وبعدها نضيف class باسم SwaggerBasicAuthMiddleware بالشكل التالي:

الكود الخاص بهذا class هو :
public class SwaggerBasicAuthMiddleware
{
private readonly RequestDelegate next;
public SwaggerBasicAuthMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/swagger"))
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
// Get the credentials from request header
var header = AuthenticationHeaderValue.Parse(authHeader);
var inBytes = Convert.FromBase64String(header.Parameter);
var credentials = Encoding.UTF8.GetString(inBytes).Split(':');
var username = credentials[0];
var password = credentials[1];
// validate credentials
if (username.Equals("Layan")
&& password.Equals("Lareen"))
{
await next.Invoke(context).ConfigureAwait(false);
return;
}
}
context.Response.Headers["WWW-Authenticate"] = "Basic";
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
await next.Invoke(context).ConfigureAwait(false);
}
}
}
نوخذ فكره سريعة عن الكود:
عرفنا في البداية متغير من النوع RequestDelegate الي من خلالو بتم معالجة الطلبات من نوع http وبعدها عملنا injection لها المتغير في constructor
الكود:
private readonly RequestDelegate next;
public SwaggerBasicAuthMiddleware(RequestDelegate next)
{
this.next = next;
}
بعدها انشاءنا function واستقبلنا فيه متغير من نوع HttpContext وبعدها رجعنا Path الخاص ب swagger. بعد هيك تم التأكد من Header إذا في طلب او لا باستخدام الكود:
string authHeader = context.Request.Headers["Authorization"];
بعد هيك نتأكد من header اذا ما كان null او كان اذا كان يبدأ ب Basic . وبعد هيك قرأنا البيانات من Header وتأكدنا من اسم المستخدم وكلمة المرور. اذا كانت النتيجة صحيحه بتم عرض الصفحة باستخدام الكود:
await next.Invoke(context).ConfigureAwait(false);
اذا كانت القيم غير صحيحه بتم ارجاع خطأ باستخدام الكود:
context.Response.Headers["WWW-Authenticate"] = "Basic";
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
تمام الان بنعمل extension method لاستخدام هذه الكود
اضف class جديد باسم SwaggerAuthorizeExtensions وبنضيف الكود التالي :
public static class SwaggerAuthorizeExtensions
{
public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SwaggerBasicAuthMiddleware>();
}
}
اضافنا function باسم UseSwaggerAuthorized واضفنا الكود التالي :
return builder.UseMiddleware<SwaggerBasicAuthMiddleware>();
في هذا function تم استدعاء class السابق SwaggerBasicAuthMiddleware
تمام الان نطبق هذا الحكي في startup.cs
بنضيف الكود التالي في Configure في ملف startup.cs
app.UseAuthorization();
app.UseSwaggerAuthorized();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SecureSwagger v1"));
من المهم الترتيب في تطبيق الكود السابق تأكد من إضافة ()UseSwaggerAuthorized قبل استدعاء ()UseSwagger و ()UseSwaggerUI ، الهدف من هذي الحركة هو استدعاء middleware المسؤول عن التحقق من اسم المستخدم وكلمة المرور قبل الوصول إلى واجهة المستخدم swagger ui.
شغل الصفحة واكيد بتكون النتيجة كما في الصورة.

اذا تم ادخال بيانات خطأ بتم الرجاع خطأ من نوع : 401 error page

الكود الكامل هنا.
SwaggerBasicAuthMiddleware class
public class SwaggerBasicAuthMiddleware
{
private readonly RequestDelegate next;
public SwaggerBasicAuthMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/swagger"))
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
// Get the credentials from request header
var header = AuthenticationHeaderValue.Parse(authHeader);
var inBytes = Convert.FromBase64String(header.Parameter);
var credentials = Encoding.UTF8.GetString(inBytes).Split(':');
var username = credentials[0];
var password = credentials[1];
// validate credentials
if (username.Equals("Layan")
&& password.Equals("Lareen"))
{
await next.Invoke(context).ConfigureAwait(false);
return;
}
}
context.Response.Headers["WWW-Authenticate"] = "Basic";
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
await next.Invoke(context).ConfigureAwait(false);
}
}
}
SwaggerAuthorizeExtensions class
public static class SwaggerAuthorizeExtensions
{
public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SwaggerBasicAuthMiddleware>();
}
}
Configure function
app.UseAuthentication();
app.UseSwaggerAuthorized();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "DietWrold.API v1"));
اترك تعليقك