JWT, anlamına gelir ve kimlik doğrulama değil, bir yetkilendirme mekanizmasıdır. Şimdi bu ikisi arasındaki farkın ne olduğunu bulalım. JSON Web Token kullanıcının tam olarak iddia ettiği kişi olduğunun doğrulanmasını sağlayan mekanizmadır. Kullanıcının kullanıcı adı ve şifre sağladığı ve sistemin bunları doğruladığı bir oturum açma işlemidir. Kimlik doğrulama şu soruyu yanıtlıyor: Kullanıcı kim? Kimlik doğrulama, , kullanıcının belirli bir kaynağa hangi erişim haklarına sahip olduğunun doğrulanmasını sağlayan mekanizmadır. Kullanıcılara bazı roller ve belirli bir rolün sahip olduğu bir dizi izin verme sürecidir. Yetkilendirme şu soruyu yanıtlıyor: Kullanıcı sistemde hangi haklara sahiptir? Yetkilendirme Kimlik Doğrulamanın her zaman önce geldiğini ve Yetkilendirmenin ikinci sırada geldiğini anlamak önemlidir. Yani kimliğinizi doğrulamadan izin alamazsınız. Peki en popüler yetkilendirme yöntemleri nelerdir? Web uygulaması için yetkilendirmeyi ele almada iki ana yaklaşım vardır. Oturumlar Kullanıcıların yetkilendirilmesi için web üzerinde geleneksel bir yaklaşım, çerez tabanlı sunucu tarafı oturumudur. İşlem, bir kullanıcı oturum açtığında ve bir sunucu onun kimliğini doğruladığında başlar. Bundan sonra sunucu, Oturum Kimliğiyle bir oturum oluşturur ve bunu sunucunun belleğinde bir yerde saklar. Sunucu, Oturum Kimliğini istemciye geri gönderir ve istemci, Oturum Kimliğini çerezlerde saklar. Her istek için istemci, isteğin bir parçası olarak bir Oturum Kimliği gönderir ve sunucu, hafızasındaki Oturum Kimliğini ve kullanıcının bu oturumla ilgili izinlerini doğrular. Jetonlar Bir diğer popüler yaklaşım ise yetkilendirme için token kullanmaktır. İşlem, kullanıcı oturum açma adını ve parolalarını girdiğinde ve istemci sunucuya oturum açma isteği gönderdiğinde benzer şekilde başlar. Sunucu, bir oturum oluşturmak yerine gizli belirteçle imzalanmış bir belirteç oluşturur. Daha sonra sunucu, belirteci istemciye geri gönderir ve istemcinin bunu yerel bir depolama biriminde saklaması gerekir. Oturum tabanlı yaklaşıma benzer şekilde, istemcinin her istek için sunucuya bir belirteç göndermesi gerekir. Ancak sunucu, kullanıcı oturumuna ilişkin herhangi bir ek bilgi saklamaz. Sunucunun, jetonun oluşturulduğundan ve gizli anahtarla imzalandığından beri değişmediğini doğrulaması gerekir. Oturum ve Jeton Karşılaştırması Oturum tabanlı yetkilendirme yaklaşımı, Siteler Arası İstek Sahteciliği (CSRF) olarak bilinen bir saldırıya karşı savunmasız olabilir. Saldırganın, ödeme yapmak veya şifre değiştirmek gibi amaçlamadığı eylemleri gerçekleştirmek için oturum açtığı bir siteyi işaret etmesi bir tür saldırıdır. Başka bir şey, oturum tabanlı yetkilendirme yaklaşımı kullanıldığında, istemci ile sunucu arasında durum bilgisi olan bir oturum oluşturulmasıdır. Sorun, bir istemcinin aynı uygulama kapsamındaki farklı sunuculara erişmek istemesi durumunda, bu sunucuların bir oturum durumunu paylaşmak zorunda olmasıdır. Başka bir durumda, oturum farklı olacağından istemcinin her sunucuda yetkilendirilmesi gerekecektir. Öte yandan token tabanlı yetkilendirme yaklaşımı, oturum verilerinin sunucu tarafında saklanmasını gerektirmez ve birden fazla sunucu arasındaki yetkilendirmeyi kolaylaştırabilir. Ancak tokenlar yine de bir saldırgan tarafından çalınabilir ve tokenları geçersiz kılmak da zor olabilir. Bu makalede ayrıntıları ve geçersiz kılmanın nasıl ele alınacağını daha ayrıntılı olarak göreceğiz. JWT JSON Web Token (JWT), taraflar arasında bilgilerin bir JSON nesnesi olarak güvenli bir şekilde iletilmesi için kompakt ve bağımsız bir yol tanımlayan açık bir standarttır. Bu bilgiler dijital olarak imzalandığı için doğrulanabilir ve güvenilir olabilir. JWT'ler bir sır ( algoritmasıyla) veya veya kullanılarak genel/özel anahtar çifti kullanılarak imzalanabilir. HMAC RSA ECDSA JWT yapısı JSON Web Tokenları noktalarla ayrılmış üç bölümden oluşur . Başlık { "alg": "HS256", "typ": "JWT" } Başlık genellikle iki bölümden oluşur: belirtecin türü ve kullanılan imzalama algoritması. Yük { "sub": "1234567890", "name": "John Doe", "admin": true } Yük, kullanıcı hakkındaki ifadeler olan talepleri içerir. Yük daha sonra JSON Web Token'ın ikinci bölümünü oluşturmak için olarak kodlanır. Talep olarak kullanılan standart alanların açıklamasını bulabilirsiniz. Base64Url burada İmza HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) İmza kısmını oluşturmak için kodlanmış başlığı, kodlanmış veriyi, bir sırrı ve başlıkta belirtilen algoritmayı alıp imzalamanız gerekiyor. Belirteç genellikle aşağıdaki gibi görünür: xxxxx.yyyyy.zzzzz gidebilir ve örnek bir jetonun veya kendinizinkinin hatalarını ayıklayabilirsiniz. Belirtecinizi alana yapıştırın ve belirteç imzasının seçin. jwt.io'ya Kodlanmış Algoritmasını .NET projesi Artık JWT'nin nasıl çalıştığına dair teorik bilgiye sahip olduğumuza göre, bunu gerçek hayattaki projelere uygulayabiliriz. Kahve varlığı için CRUD işlemlerini temsil eden basit bir API'ye sahip olduğumuzu varsayalım. Coffee API'yi temsil eden bir ASP.NET Core API projesi oluşturacağız. Bundan sonra JWT oluşturabilecek bir Identity API'yi temsil edecek başka bir ASP.NET Core API projesi oluşturacağız. Gerçek hayatta, Kimlik Doğrulama/Yetkilendirme amacıyla muhtemelen veya veya kullanırsınız. Ancak JWT'nin nasıl oluşturulacağını göstermek için kendi Kimlik API'mizi oluşturacağız. Identity API tamamlandığında, denetleyicisini çağırabilir ve kullanıcının verilerine göre JWT oluşturabiliriz. Ayrıca Coffee API'yi, her istekte JWT'nin geçirilmesini gerektiren bir yetkilendirme yapılandırmasıyla koruyabiliriz. Identity Server Okta Auth0 Kahve API'si Öncelikle Coffee API'yi temsil eden basit bir ASP.NET Core API projesi oluşturacağız. İşte bu projenin yapısı: klasöründeki ile başlayalım. ve özelliklerine sahip basit bir varlıktır. Model Coffee.cs Id Name namespace Hackernoon.Coffee.API.Model; public class Coffee { public int Id { get; set; } public string Name { get; set; } } API ile çalışırken varlıklarımızı saklamamız gerekiyor. Öyleyse basit bir bellek içi depolamayı tanıtalım. klasöründeki dosyasında bulunur. Data Storage.cs namespace Hackernoon.Coffee.API.Data; public static class Storage { private static readonly List<Model.Coffee> Data = new(); public static List<Model.Coffee> GetAll() { return Data; } public static bool Create(Model.Coffee model) { if (Data.Any(c => c.Id == model.Id || c.Name == model.Name)) return false; Data.Add(new Model.Coffee { Id = model.Id, Name = model.Name }); return true; } public static bool Delete(int id) { if (Data.All(c => c.Id != id)) return false; Data.Remove(Storage.Data.First(c => c.Id == id)); return true; } public static bool Update(Model.Coffee model) { if (Data.All(c => c.Id != model.Id)) return false; Data.First(c => c.Id == model.Id).Name = model.Name; return true; } } Coffee API'ye gelen istekleri temsil edecek bir sınıfa ihtiyacımız var. Şimdi klasöründe dosyasını oluşturalım. Contracts CoffeeRequest.cs namespace Hackernoon.Coffee.API.Contracts; public class CoffeeRequest { public int Id { get; set; } public string Name { get; set; } } Tamamlandığında, kahve varlığı için CRUD işlemlerini temsil eden klasörüne uygulayabiliriz. CoffeeController.cs Controller using Hackernoon.Coffee.API.Contracts; using Hackernoon.Coffee.API.Data; using Microsoft.AspNetCore.Mvc; namespace Hackernoon.Coffee.API.Controllers; [Route("coffee")] [ApiController] public class CoffeeController : ControllerBase { [HttpGet] public IList<Model.Coffee> GetAll() { return Storage.GetAll(); } [HttpPost] public IActionResult Create([FromBody]CoffeeRequest request) { var model = new Model.Coffee { Id = request.Id, Name = request.Name }; if (!Storage.Create(model)) return new BadRequestResult(); return new OkResult(); } [HttpDelete] public IActionResult Delete(int id) { if (!Storage.Delete(id)) return new BadRequestResult(); return new OkResult(); } [HttpPut] public IActionResult Update([FromBody] CoffeeRequest request) { var model = new Model.Coffee() { Id = request.Id, Name = request.Name }; if (!Storage.Update(model)) return new BadRequestResult(); return new OkResult(); } } Coffee API tamamlandı ve projeyi çalıştırıp Swagger UI'yi aşağıdaki gibi görebiliriz: Kimlik API'si Identity API'yi temsil eden başka bir ASP.NET Core API projesi oluşturalım. İşte bu projenin yapısı: ve özelliklerine sahip yeni bir JWT oluşturma isteğini temsil eden klasöründeki ile başlayalım. Email Password Contracts TokenGenerationRequest.cs namespace Hackernoon.Identity.API.Contracts; public class TokenGenerationRequest { public string Email { get; set; } public string Password { get; set; } } Yalnızca JWT oluşturma mantığını temsil eden uygulamamız gerekiyor. Ancak bunu yapmadan önce NuGet paketinin kurulması gerekiyor. TokenController.cs Microsoft.AspNetCore.Authentication.JwtBearer using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Hackernoon.Identity.API.Contracts; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; namespace Hackernoon.Identity.API.Controllers; [Route("token")] public class TokenController : ControllerBase { private const string SecretKey = "VerySecretAndLongKey-NeedMoreSymbolsHere-123"; private const string Issuer = "IdentityServerIssuer"; private const string Audience = "IdentityServerClient"; private static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(20); [HttpPost] public string Create([FromBody]TokenGenerationRequest request) { var claims = new List<Claim> {new Claim(ClaimTypes.Email, request.Email) }; var jwt = new JwtSecurityToken( issuer: Issuer, audience: Audience, claims: claims, expires: DateTime.UtcNow.Add(Lifetime), signingCredentials: CreateSigningCredentials()); return new JwtSecurityTokenHandler().WriteToken(jwt); } private static SigningCredentials CreateSigningCredentials() { return new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)), SecurityAlgorithms.HmacSha256); } } , ve gibi hassas yapıların yapılandırmada bir yere yerleştirilmesi gerektiğini unutmayın. Sadece bu test projesini basitleştirmek için sabit kodlanmıştır. alanı 20 dakikaya ayarlanmıştır, bu da belirtecin o süre boyunca geçerli olacağı anlamına gelir. Bu parametreyi de yapılandırabilirsiniz. SecretKey Issuer Audience Lifetime Artık projeyi çalıştırıp Swagger kullanıcı arayüzünü aşağıdaki gibi görebiliriz: uç noktasına bir çağrı yapalım ve yeni bir JWT oluşturalım. Aşağıdaki yükü deneyin: /token { "email": "john.doe@gmail.com", "password": "password" } Kimlik API'si karşılık gelen JWT'yi oluşturacaktır: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJqb2huLmRvZUBnbWFpbC5jb20iLCJJc0dvdXJtZXQiOiJmYWxzZSIsImV4cCI6MTcwNzc4Mzk4MCwiaXNzIjoiSWRlbnRpdHlTZXJ2ZXJJc3N1ZXIiLCJhdWQiOiJJZGVudGl0eVNlcnZlckNsaWVudCJ9.4odXsbWak1C0uK3Ux-n7f58icYQQwlHjM54OjgMCVPM Coffee API'de Yetkilendirmeyi Etkinleştirme Artık Identity API hazır olduğunda ve bize token sağladığında Coffee API'yi yetkilendirme ile koruyabiliriz. Yine NuGet paketinin kurulması gerekiyor. Microsoft.AspNetCore.Authentication.JwtBearer Gerekli hizmetleri kimlik doğrulama hizmetleriyle kaydetmemiz gerekiyor. Bir oluşturucu oluşturduktan hemen sonra dosyasına aşağıdaki kodu ekleyin. Program.cs var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "IdentityServerIssuer", ValidateAudience = true, ValidAudience = "IdentityServerClient", ValidateLifetime = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes("VerySecretAndLongKey-NeedMoreSymbolsHere-123")), ValidateIssuerSigningKey = true, }; }); builder.Services.AddAuthorization(); Ara katman yazılımındaki sıranın önemli olduğunu hatırlamak önemlidir. yöntemini çağırıp kimlik doğrulama şeması olarak belirterek kimlik doğrulamayı etkinleştiriyoruz. değeri içeren bir sabittir. AddAuthentication() JwtBearerDefaults.AuthenticationScheme Bearer namespace Microsoft.AspNetCore.Authentication.JwtBearer { /// <summary>Default values used by bearer authentication.</summary> public static class JwtBearerDefaults { /// <summary> /// Default value for AuthenticationScheme property in the JwtBearerAuthenticationOptions /// </summary> public const string AuthenticationScheme = "Bearer"; } } Yetkilendirme sırasında JWT'nin hangi parametrelerinin doğrulanacağını açıklayan belirtmemiz gerekiyor. Ayrıca JWT imzasını doğrulamak için Identity API'dekisigningCredentials'a belirtiriz. hakkında daha fazla ayrıntıyı kontrol edin. TokenValidationParameters signingCredentials IssuerSigningKey TokenValidationParameters buradan Bir sonraki kod parçası, oluşturucuya kimlik doğrulama ve yetkilendirme yeteneklerini etkinleştiren ara yazılım ekler. ve yöntemleri arasına eklenmelidir. UseHttpsRedirection() MapControllers() app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); Artık Controller veya eylemleri üzerinde niteliğini kullanabiliriz. Bu kodun uygulanmasıyla artık tüm eylemler bir yetkilendirme mekanizmasıyla korunuyor ve isteğin bir parçası olarak JWT'nin gönderilmesi gerekiyor. Authorize CoffeeController [Route("coffee")] [ApiController] [Authorize] public class CoffeeController : ControllerBase { .. Coffee API'nin herhangi bir uç noktasına çağrı yaparsak, hata ayıklayabilir ve JWT'de belirttiğimiz iddialarla doldurulduğunu ve bir sahip olduğunu görebiliriz. ASP.NET Core'un Yetkilendirmeyi nasıl ele aldığını anlamak önemli bir şeydir. HttpContext.User Identity Swagger Kullanıcı Arayüzüne Yetki Ekleme Yetkilendirme ile Coffee API'yi korumak için harika çalışmalar yaptık. Ancak Coffee API projesini çalıştırırsanız ve Swagger UI'yi açarsanız isteğin bir parçası olarak JWT gönderemezsiniz. Bunu düzeltmek için dosyasını aşağıdaki kodla güncellememiz gerekiyor: Program.cs builder.Services.AddSwaggerGen(option => { option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { In = ParameterLocation.Header, Description = "Please enter a valid token", Name = "Authorization", Type = SecuritySchemeType.Http, BearerFormat = "JWT", Scheme = "Bearer" }); option.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type=ReferenceType.SecurityScheme, Id="Bearer" } }, new string[]{} } }); }); Bundan sonra sağ üst köşede butonunu görebileceğiz: Yetkilendir butonuna tıkladığınızda JWT'ye aşağıdaki gibi girebileceksiniz: Yetkilendir Test için Postman'ı kullanın Kendinizi Swagger UI'yi kullanmakla sınırlayamazsınız ve API'nin testini Postman aracı aracılığıyla gerçekleştirebilirsiniz. Önce Identity API'nin uç noktasını çağıralım. Payload olarak JSON kullanacağımız için kısmında başlığını değeriyle belirtmemiz gerekiyor. /token Headers Content-Type application/json Bundan sonra uç noktasını çağırabilir ve yeni bir JWT alabiliriz. /token Artık JWT'yi kopyalayıp Coffee API'yi çağırmak için kullanabiliriz. Uç noktaları test etmek, oluşturmak ve güncellemek istiyorsak Identity API'ye benzer başlığını belirtmemiz gerekir. başlığının ayrıca değeriyle ayarlanması gerekir. Bundan sonra düğmesine basın ve sonucu görün. Content-Type Authorization Bearer [your JWT value] Gönder Rol bazlı yetkilendirme Hatırlayacağınız gibi JWT'nin yük kısmı, tam olarak anahtar/değer çiftleri olan değerlere sahip bir dizi talepten oluşur. Rol tabanlı yetkilendirme, kullanıcının ait olduğu role bağlı olarak uygulama kaynaklarına erişimi farklılaştırmanıza olanak tanır. Identity API'deki dosyasındaki metodunu, role yeni bir talep ekleyen kodla güncellersek; Coffee API'de rol tabanlı kimlik doğrulamayı gerçekleştirebiliriz. , rol talebinin önceden tanımlanmış adıdır. TokenController.cs Create() ClaimTypes.Role var claims = new List<Claim> { new Claim(ClaimTypes.Email, request.Email), new Claim(ClaimTypes.Role, "Barista") }; Rol adını belirterek dosyasındaki özelliğini güncelleyin: CoffeeController.cs Authorize [Authorize(Roles = "Barista")] Artık Coffee API'ye çağrı yapan tüm kullanıcıların değerine sahip rol talebinde bulunması gerekiyor. Aksi takdirde durum kodu alacaklardır. Barista 403 Forbidden Talebe dayalı yetkilendirme özelliği, rol tabanlı kimlik doğrulamayı kolayca işleyebilir. Peki ya bu yeterli değilse ve erişimi yaş gibi bazı kullanıcı özelliklerine göre farklılaştırmak istiyorsak? Muhtemelen taleplerinizi JWT'ye ekleyebileceğinizi ve bunları yetkilendirme mantığı oluşturmak için kullanabileceğinizi tahmin etmişsinizdir. Rol tabanlı yetkilendirmenin kendisi, talep tabanlı yetkilendirmenin özel bir durumudur; tıpkı rolün önceden tanımlanmış türdeki aynı talep nesnesi olması gibi. Authorize Identity API'deki dosyasındaki yöntemini yeni bir iddia ekleyen koduyla güncelleyelim. TokenController.cs Create() IsGourmet var claims = new List<Claim> { new Claim(ClaimTypes.Email, request.Email), new Claim("IsGourmet", "true") }; Coffee API'deki Program.cs dosyasında bir iddiayı doğrulayan ve özelliğinde kullanılabilecek bir politika oluşturmamız gerekiyor. yöntem çağrısından hemen sonra aşağıdaki kodun eklenmesi gerekir. Authorize AddAuthentication() builder.Services.AddAuthorization(opts => { opts.AddPolicy("OnlyForGourmet", policy => { policy.RequireClaim("IsGourmet", "true"); }); }); İlke adını belirterek dosyasındaki özniteliğini güncelleyin: CoffeeController.cs Authorize [Authorize(Policy = "OnlyForGourmet")] Özet Tebrikler! .NET'te JWT'yi öğrenmek için büyük çaba harcadınız. Artık JWT ilkelerini ve .NET uygulamalarında yetkilendirme gerçekleştirmek için onu kullanmanın neden önemli olduğunu iyice anlamış olmanız gerekir. Ancak ASP.NET Core uygulamalarında kimlik doğrulama ve yetkilendirme alanında henüz yeni bir başlangıç yaptık. Bu makalede tartıştığımız konularla ilgili Microsoft belgelerine bakmanızı öneririm. .NET platformunda yetkilendirme ve rol yönetimi için de birçok yerleşik yetenek bulunmaktadır. Yetkilendirmeyle ilgili Microsoft bu makaleye iyi bir katkı olabilir. belgeleri