RRedis Handbook

UZMAN

Session Management

Multi-instance uygulama session'larının Redis ile merkezi yönetimi.

Kod örneği görünümü Bu sayfadaki eşleşen örnekleri seçilen istemciye göre gösterir.
Login HSET + EXPIRE Active Session sliding TTL 30m Request EXPIRE refresh Expired TTL = 0 → silindi Logout DEL explicit her request TTL yeniler

Ne Zaman Redis Session Kullan / Kullanma

Kullan Kullanma Gerçek Hayat
Multi-instance/pod uygulama — session paylaşımı gerekli Tek pod, in-memory session yeterli SaaS: 5 pod arkasında LB — hangi pod'a düşerse düşsün session geçerli
Sticky session istemiyorsun (LB esnekliği) JWT-only auth (stateless, session yok) E-ticaret: Pod restart'ta kullanıcı çıkış yapmaz
Session içeriği küçük (userId, role, preferences) Büyük objeleri session'da tutmak (dosya, sepet detayı) Chat app: Online status + last-seen → küçük Hash, sliding TTL
Hızlı invalidation gerekli (force logout, güvenlik) Sadece login/logout, arada session'a erişim yok Fintech: Fraud algılandığında anında tüm session'ları DEL

Gerçek hayat senaryosu — Force logout: Admin panelden kullanıcıyı "tüm cihazlardan çıkar" yapmak istiyorsun. Redis'te SCAN session:* MATCH *userId:1001* + DEL ile tüm session'larını anında sil. JWT token'ları expire olana kadar geçerli kalır — Redis session ise anında invalidate edilir.

HSET session:abc123 userId "1001" role "admin" loginAt "1716825600"
EXPIRE session:abc123 1800    # 30dk inactivity timeout
# Her request'te:
EXPIRE session:abc123 1800    # sliding expiration
HGETALL session:abc123
// Program.cs — Redis-backed distributed session
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "session:";
});

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Strict;
});

// Custom session store (daha fazla kontrol)
public class RedisSessionService
{
    private readonly IDatabase _redis;

    public RedisSessionService(IConnectionMultiplexer mux)
        => _redis = mux.GetDatabase();

    public async Task CreateSessionAsync(string sessionId, SessionData data)
    {
        var key = $"session:{sessionId}";
        await _redis.HashSetAsync(key, new HashEntry[]
        {
            new("userId", data.UserId),
            new("role", data.Role),
            new("loginAt", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())
        });
        await _redis.KeyExpireAsync(key, TimeSpan.FromMinutes(30));
    }

    public async Task<SessionData?> GetSessionAsync(string sessionId)
    {
        var key = $"session:{sessionId}";
        var entries = await _redis.HashGetAllAsync(key);
        if (entries.Length == 0) return null;

        // Sliding expiration
        await _redis.KeyExpireAsync(key, TimeSpan.FromMinutes(30));

        var dict = entries.ToDictionary(e => e.Name.ToString(), e => e.Value.ToString());
        return new SessionData
        {
            UserId = dict.GetValueOrDefault("userId") ?? "",
            Role = dict.GetValueOrDefault("role") ?? ""
        };
    }

    public async Task DestroySessionAsync(string sessionId)
    {
        await _redis.KeyDeleteAsync($"session:{sessionId}");
    }

    // Session Fixation koruması — login/role değişiminde ID yenile
    public async Task<string> RotateSessionAsync(string oldSessionId)
    {
        var oldKey = $"session:{oldSessionId}";
        var entries = await _redis.HashGetAllAsync(oldKey);
        if (entries.Length == 0)
            throw new InvalidOperationException("Session not found");

        // Yeni session ID üret
        var newSessionId = Convert.ToBase64String(
            RandomNumberGenerator.GetBytes(32));
        var newKey = $"session:{newSessionId}";

        // Veriyi yeni key'e taşı
        await _redis.HashSetAsync(newKey, entries);
        await _redis.KeyExpireAsync(newKey, TimeSpan.FromMinutes(30));

        // Eski session'ı sil
        await _redis.KeyDeleteAsync(oldKey);

        return newSessionId;
    }
}

Session Fixation: Kullanıcı login olduğunda veya yetki seviyesi değiştiğinde mutlaka RotateSessionAsync çağır. Eski session ID'yi geçersiz kıl — aksi halde saldırgan eski ID ile oturum ele geçirebilir (OWASP A07).