ASP.NET Core 6/Oturum Yönetimi
Oturum yönetimi de bir önceki derste gördüğümüz cookie'lerle aynı işi yapar. Aynı istemci tarafından gönderilen ardışık istekleri birbiriyle ilişkilendirir. Ancak cookie yaklaşımının sorunu oturumla ilgili bütün verilerin sürekli sunucu ve istemci arasında gidip gelmesidir. Bir önceki dersteki örnekte tarayıcınızla cookie değişkenlerini incelerseniz cookie'lerin değerlerini görebilirsiniz. Halbuki buna gerek yoktur. İstemcinin kimliğini tespit etmek için tek bir cookie kullanılması, onun dışındaki kullanıcının oturumla ilgili bütün verilerinin sunucu tarafında tutulması daha mantıklıdır. Bu hem gereksiz veri alışverişini engeller hem de bir güvenlik zaafiyetini ortadan kaldırır. Bütün oturum verilerinin istemci ve sunucu arasında gidip geldiği durumda istemci bu cookie'lere istediği gibi müdahale edebilir. Örneğin kullanıcının bir sayfaya erişme yetkisinin olup olmadığı bilgisi cookie'ler üzerinde tutuluyorsa istemci bu cookie'leri değiştirebildiği için yetkisi olmayan bir sayfayı görebilir.
Oturum yönetimi kullanıldığı zaman istemci ve sunucu arasında gidip gelen şey tek bir cookie'dir. Bu cookie kullanıcıyı diğerlerinden ayırt etmeye yarar ve esasında uzunca bir sayıdır. Şimdi şunu merak edebilirisiniz, sunucuya gönderdiğim cookie'de başka bir kullanıcının kimliğini tespit etmek için kullanılan sayıyı göndersem sunucuyu kandırabilir miyim? Bu çok çok düşük bir ihtimaldir ve brute-force tekniğiyle rastgele birisi için kullanılan bir sayıyı denk getirme ihtimaliniz çok düşüktür. Bunun yerine klasik usulde kullanıcı adı ve şifre denemek daha mantıklıdır. Ayrıca çoğu sunucu aynı istemciden kısa aralıklarla çok fazla sayıda gelen talepleri tespit eder ve bloklar (veya captcha doğrulaması ister).
Oturum yönetiminden bir middleware sorumludur. Kimlik tespiti için kullanılan cookie'yi her response'ta response'a ekler. Her request'te cookie'yi inceler, cookie'deki kimlik tespiti için kullanılan sayıyı bulur, daha sonra bu sayıya karşılık gelen ve sunucu tarafında saklanan kullanıcı verilerini diğer middleware ve endpoint'ler tarafından kolayca erişilebilecek şekilde tutar.
Oturum servisi ve middleware'inin konfigüre edilmesi
[değiştir]Örnek (Program.cs dosyası):
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(opts => {
opts.IdleTimeout = TimeSpan.FromMinutes(30);
opts.Cookie.IsEssential = true;
});
var app = builder.Build();
app.UseSession();
app.MapFallback(async context =>
await context.Response.WriteAsync("Hello World!"));
app.Run();
Oturum yönetiminde cookie yaklaşımından farklı olarak sunucu tarafında bir şeyler saklamamız gerekiyor. İşte
builder.Services.AddDistributedMemoryCache();
satırında oturumla ilgili verilerin RAM bellekte saklanacağı belirtiliyor. Bu en basit yaklaşımdır, ancak sunucu kapanırsa veya yeniden başlatılırsa depolanan oturumla ilgili bilgilerin kaybolacağı anlamına gelir. Bunun gibi başka servisler de vardır ve oturum verilerini başka yerlerde saklamaya yararlar. Bunlar:
AddDistributedMemoryCache(): Oturum verilerini RAM bellekte depolar.
AddDistributedSqlServerCache(): Oturum verilerini SQL Server'da saklar.
AddStackExchangeRedisCache(): Oturum verilerini Redis isimli veritabanı yönetim sisteminde saklar.
Bir sonraki adım oturum middleware'inin yapılandırılmasıdır ve aşağıdaki kodlarla olur:
builder.Services.AddSession(opts => {
opts.IdleTimeout = TimeSpan.FromMinutes(30);
opts.Cookie.IsEssential = true;
});
Oturum middleware'inin ayar sınıfı SessionOptions'tır ve önemli özellikleri aşağıda belirtilmiştir:
Cookie: Oturum cookie'sinin özelliklerini almaya/değiştirmeye yarar.
IdleTimeout: Oturumun atıl kalma süresini belirtmeye yarar. Kullanıcı belirtilen süre boyunca sunucuya talepte bulunmazsa oturum cookie'si silinir ve kullanıcının yeniden oturum açması gerekir.
Cookie özelliğinin oturum cookie'sinin özelliklerini değiştirmeye yaradığını söylemiştik. Bu özellik CookieBuilder tipinde bir nesne döndürür. Bu sınıfın önemli özellikleri şunlardır:
HttpOnly: Eğer değeri true yapılırsa yalnızca HTTP protokolü üzerinden gelen isteklerde oturum değişkenleri korunur. Aynı istemci tarafından gelse bile JavaScript tarafından istek gönderildiği zaman oturum değişkenleri korunmaz. Örneğin Ajax teknolojisi kullanılarak JavaScript üzerinden arkaplanda sunucunun veritabanından birtakım verilerin çekilmeye çalışıldığı durumda kimlik doğrulanamayacağı için başarısız olunur. Varsayılanı true'dur.
IsEssential: İlgili cookie'nin esas bir cookie olduğunu belrtir. Varsayılanı false'tur.
SecurityPolicy: Cookie'nin güvenlik politikasını belirtir. CookieSecurityPolicy enumu aracılığıyla değer verilir. Bu enumdaki sözcükler şunlardır:
- Always: Cookie sadece HTTPS bağlantılarında gönderilir. HTTP bağlantılarında cookie kullanılamaz.
- SameAsRequest: İlk request HTTPS üzerinden yapılmışsa diğer iletişimin de HTTPS üzerinden yapılmasını gerektirir. Aksi durumda cookie kullanılamaz.
- None: Herhangi bir kısıtlama yapılmaz. Cookie hem HTTP hem de HTTPS bağlantılarda kullanılabilir. Varsayılanı None'dır.
- Always: Cookie sadece HTTPS bağlantılarında gönderilir. HTTP bağlantılarında cookie kullanılamaz.
Örneğimize geri dönersek örneğimizdeki oturum cookie'si için yapılan tek ayar cookie'nin esas bir cookie olduğunun belirtilmesidir. Son olarak yapılan işlem oturum middleware'inin request pipeline'a eklenmesidir. Oturum middleware'inin request pipeline'a eklenmesi sayesinde kullanıcı ilk kez sunucuya talepte bulunduğunda istemciye kullanıcıyı ayırt etmeye yarayan cookie gönderilir, daha sonra cookie gönderdiği her bir kullanıcı için RAM bellekte tablo tutar. Diğer middleware ve endpoint'ler bu tabloya yazdığında tabloyu günceller, her gelen request'te request'i gönderen kullanıcının tablosunda veriler varsa request'i temsil eden HttpContext nesnesinin Session özelliğini bu tablodaki değerlerle doldurur.
Diğer middleware ve endpoint'lerin bu tabloya yazıp okumasına imkan tanır.
Oturum verisinin kullanımı
[değiştir]Şimdiye kadar bir oturum başlattık ama oturuma yazıp okumadık. Oturum middleware'i sayesinde oturuma yazıp okumak çok kolay olacak. Oturuma yazıp okumak HttpContext nesnesinin Session özelliği aracılığıyla olur. Session özelliği ISession arayüzünü uygulamış bir sınıf nesnesi döndürür. ISession arayüzünün önemli üyeleri şunlardır:
Clear(): Oturumu siler.
CommitAsync(): Oturuma yazılmak üzere bekleyen değişiklikleri oturuma yazar.
GetString(key): key anahtarıyla temsil edilen oturum değişkenini string olarak döndürür.
GetInt32(key): key anahtarıyla temsil edilen oturum değişkenini int olarak döndürü.
Id: Geçerli oturumun id'sini döndürür.
IsAvailable: Oturum verisi okunmaya müsaitse true döndürür.
Keys: Oturum değişkenlerinin anahtarlarını döndürür.
Remove(key): İlgili anahtarla temsil edilen oturum değişkenini siler.
SetString(key,val): key anahtarıyla belirtilen oturum değişkeninin değerini string olarak belirtir.
SetInt32(key,val): key anahtarıyla belirtilen oturum değişkeninin değerini int olarak belirtir.
string ve int olmak üzere iki tip oturum değişkeni saklanabilir. Oturum değişkenlerine string tipindeki anahtarlar vasıtasıyla erişilir ve yazılır. Örnek (Program.cs dosyası):
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(opts => {
opts.IdleTimeout = TimeSpan.FromMinutes(30);
opts.Cookie.IsEssential = true;
});
var app = builder.Build();
app.UseSession();
app.MapGet("/session", async context => {
int counter1 = (context.Session.GetInt32("counter1") ?? 0) + 1;
int counter2 = (context.Session.GetInt32("counter2") ?? 0) + 1;
context.Session.SetInt32("counter1", counter1);
context.Session.SetInt32("counter2", counter2);
await context.Session.CommitAsync();
await context.Response.WriteAsync($"Counter1: {counter1}, Counter2: {counter2}");
});
app.MapFallback(async context => await context.Response.WriteAsync("Hello World!"));
app.Run();
Bu kod tahmin ettiğiniz işi yapmaktadır. "/session" path'ına gelen her request'te oturumda tutulan counter1 ve counter2 isimli oturum değişkenlerini 1 artırmakta ve response'a yazmaktadır. İlk gelen request'te ilgili oturum değişkenleri olmayacağı için başlangıç değerini 0 almakta ve 1 artırmaktadır. Oturum değişkenlerine yapılacak bütün değişiklikler response'a yazmadan önce yapılmalıdır. Ayrıca oturum middleware'i request pipeline'a eklenmeden HttpContext nesnesi üzerinden Session özelliği çağrılmamalıdır. Çünkü bu özelliği dolduracak middleware oturum middleware'idir.
Yukarıdaki programı çalıştırdığımızda her sayfayı yenilediğimizde counter değerlerinin arttığını göreceğiz. Daha da güzeli tarayıcı üzerinden cookie'lere baktığınızda "AspNetCore.Session" isminde tek bir cookie göreceksiniz. Ben de bu cookie'nin değeri CfDJ8EGa2GuHVCVBpx5dzK3LgefaxsctRjL6mDzr+4woSaNgb5vFsUJpKQ9Z9frx2LT2n3oV/UBwTjavLX4sdEFMPCpZsvn9czEDZzQRVscXini3rwCl5t9fbZjNueJ+rz0yu8P+p+DJJOGT/zSFQU4c0hpWKwP1Zlqe+wYCxOF2I3RN idi. Teyit edeceğiniz üzere bir kişinin hasbelkader bu değeri denk getirmesi düşük bir ihtimal gibi gözüküyor.