Merhaba arkadaşlar, bu yazımda sizlere .Net Core API Basic Authentication konusundan bahsedeceğim.
Web uygulamaları, kullanıcı kimlik doğrulama ve yetkilendirme mekanizmalarıyla güvenliklerini sağlarlar. .NET Core, bu tür işlemleri kolaylaştırmak için AuthenticationHandler adlı güçlü bir sınıf sunar. Bu blog yazısında, .NET Core’da Basic Authentication’ı özelleştirmek için AuthenticationHandler kullanımını adım adım açıklayacağız.
Adım 1: Proje Oluşturma ve Paket Yüklemeleri
Öncelikle, bir .NET Core projesi oluşturun ve gerekli paketleri yükleyin. Projeyi oluşturduktan sonra aşağıdaki paketin yükleme işlemini Nuget Package Manager üzerinden gerçekleştiriyoruz.
Microsoft.AspNetCore.Authentication
Adım 2: IUserService, UserService ve UserModel Nesneslerini Oluşturma
public class UserModel
{
public string Username { get; set; }
public string Password { get; set; }
public string EmailAddress { get; set; }
}
public interface IUserService
{
List<UserModel> GetUsers();
UserModel Login(string username, string password);
}
public class UserService : IUserService
{
private readonly List<UserModel> _users;
public UserService()
{
_users = new List<UserModel>()
{
new UserModel{ Username = "user1", EmailAddress="user1@user1.com", Password="1234" },
new UserModel{ Username = "user2", EmailAddress="user2@user2.com", Password="1234" }
};
}
public List<UserModel> GetUsers()
{
return _users;
}
public UserModel Login(string username, string password)
{
var user = _users.FirstOrDefault(x => x.Username == username && x.Password == password);
return user;
}
}
Adım 3: AuthenticationHandler Oluşturma
Proje klasöründe, “Handlers” adında bir klasör oluşturun. Ardından, bu klasör içinde “BasicAuthenticationHandler.cs” adında bir sınıf dosyası oluşturun. Bu sınıf, AuthenticationHandler sınıfından miras alacak ve Basic Authentication işlemlerini gerçekleştirecektir.
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IUserService _userService;
private string? _failReason;
public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IUserService userService) : base(options, logger, encoder, clock)
{
_userService = userService;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string authorizationHeader = Request.Headers["Authorization"];
if (authorizationHeader == null || !authorizationHeader.StartsWith("Basic"))
{
_failReason = "Authorization header not found.";
return await Task.FromResult(AuthenticateResult.Fail("Authorization header not found."));
}
string encodedUsernamePassword = authorizationHeader.Substring("Basic ".Length).Trim();
byte[] decodedUsernamePassword = Convert.FromBase64String(encodedUsernamePassword);
string usernamePassword = Encoding.UTF8.GetString(decodedUsernamePassword);
string[] parts = usernamePassword.Split(':', 2);
string username = parts[0];
string password = parts[1];
bool isValid = _userService.Login(username, password) != null;
if (!isValid)
{
_failReason = "Invalid username or password.";
return await Task.FromResult(AuthenticateResult.Fail("Invalid username or password."));
}
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, username),
new Claim(ClaimTypes.Name, username)
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return await Task.FromResult(AuthenticateResult.Success(ticket));
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
await base.HandleChallengeAsync(properties);
if (Response.StatusCode == StatusCodes.Status401Unauthorized &&
!string.IsNullOrWhiteSpace(_failReason))
{
Response.Headers.Add("WWW-Authenticate", _failReason);
Response.ContentType = "application/json";
await WriteProblemDetailsAsync(_failReason);
}
}
private Task WriteProblemDetailsAsync(string detail)
{
var problemDetails = new ProblemDetails { Detail = detail, Status = Context.Response.StatusCode };
var result = new ObjectResult(problemDetails)
{
ContentTypes = new MediaTypeCollection(),
StatusCode = problemDetails.Status,
DeclaredType = problemDetails.GetType(),
};
var executor = Context.RequestServices.GetRequiredService<IActionResultExecutor<ObjectResult>>();
var routeData = Context.GetRouteData() ?? new RouteData();
var actionContext = new ActionContext(Context, routeData, new ActionDescriptor());
return executor.ExecuteAsync(actionContext, result);
}
}
Yukarıdaki kod bloğunda, BasicAuthenticationHandler sınıfı tanımlanmıştır. HandleAuthenticateAsync() metodu üzerinden gelen Authorization başlığını kontrol eder ve kullanıcı adı ve parolayı doğrular. Kullanıcı geçerli ise, gerekli kimlik bilgilerini içeren bir AuthenticationTicket döndürülür.
Adım 3: Program.cs Dosyasını Güncelleme
builder.Services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
Bu kod bloğunda, AddAuthentication() metodu kullanılarak “BasicAuthentication” adında bir AuthenticationScheme tanımlanır ve bu şemaya BasicAuthenticationHandler sınıfı eklenir.
Adım 4: Controller’da Yetkilendirme İşlemi
Bu aşamada, bir Controller içinde yetkilendirme işlemlerini gerçekleştirebilirsiniz. Örnek olarak, UserController.cs dosyasını düzenleyerek [Authorize] attirbute’unu kullanarak giriş yapılmış mı yapılmamış mı işlemini gerçekleştirebiliriz.
[HttpGet("profile")]
[Authorize(AuthenticationSchemes = "BasicAuthentication")]
public IActionResult Profile()
{
return Ok("profile basic auth");
}
Projeyi indirmek için aşağıdaki github adresini kullanabilirsiniz.
https://github.com/alicancevik/NetCoreAPIBasicAuthenticationSample
Umarım faydalı olur.
Bir sonraki yazıda görüşmek üzere…