İçeriğe geç
academia.sh

Ders 21 / 22

Oturum Yenileme

Kısa ömürlü belirtecin kullanıcıya yansımadan tazelenmesi, eşzamanlı yetki hatalarının tek bir yenilemede toplanması, yenileme belirtecinin dönüşümü ve yeniden kullanım saptaması, çıkışın her yerde etkili olması.

İçindekiler

Önceki ders belirtecin kısa ömürlü olmasını bir savunma olarak sundu. Kısa ömrün bedeli belli: on beş dakikada bir oturumun düşmesi. Gözlemci ölçüm formunu doldururken sürenin dolması, gönderimin 401 ile dönmesi ve yazılan her şeyin tehlikeye girmesi demektir.

Bu ders bedeli ödemeden kısa ömrü koruyan düzeni kurar. Kullanıcının fark etmediği bir tazeleme, aynı anda düşen birden çok isteğin tek bir tazelemede toplanması, tazeleme kanalının kendisinin korunması ve oturumun her yerde aynı anda kapanması.

Yenilemenin Üç Tetikleyicisi

Tepkisel yenileme, 401 yanıtı alındığında yapılır. İstek katmanı yanıtı görür, belirteci tazeler, isteği bir kez yineler. En basit ve en dayanıklı yoldur: sunucunun kararına dayanır, istemcinin saatine güvenmez.

Önden yenileme, sürenin dolmasına az kaldığında yapılır. Önceki dersteki süre denetimi buna hizmet eder; belirtecin exp alanı okunur ve bir pay bırakılarak erkenden tazelenir. Kazancı, kullanıcının bir isteğin iki kez gitmesini beklememesidir. Tek başına yeterli değildir — istemcinin saati yanlış olabilir, sunucu belirteci erken iptal etmiş olabilir — bu yüzden tepkisel yenilemenin yerine geçmez, onunla birlikte çalışır.

Açılıştaki sessiz oturum kurma, sayfa yenilendiğinde yapılır. Erişim belirtecini bellekte tutan bir düzende sayfa yenilendiğinde elde bir şey kalmaz; uygulama açılırken yenileme kanalını bir kez dener. Başarılı olursa oturum kaldığı yerden sürer, başarısız olursa oturum kapalı sayılır. Önceki derste anılan bilinmiyor kimlik durumu tam olarak bu deneme süresince geçerlidir.

Tek Uçuş

Bir ekran açıldığında genellikle birkaç istek birden gider: istasyon listesi, ölçüm geçmişi, özet, uyarılar. Belirtecin süresi dolduysa dördü de 401 alır. Her biri kendi yenilemesini başlatırsa sunucuya dört yenileme isteği gider — ve belirteç dönüşümü varsa üçü geçersiz belirteçle gelir, oturum kapanır.

Çözüm tek uçuş (single flight) düzenidir: yenileme başlatılırken üretilen söz saklanır, aynı anda yenileme isteyen herkes o sözü bekler.

// oturum-yenileme.mjs — sessiz yenileme, tek ucus ve yeniden kullanim saptamasi
import http from "node:http";

// --- Sunucu -----------------------------------------------------------------
let surum = 1;
let gecerliErisim = "e1", gecerliYenileme = "y1";
let oturumAcik = true, yenilemeSayaci = 0;
const kullanilmis = new Set();

const govdeyiOku = (istek) => new Promise((coz) => {
  let m = ""; istek.on("data", (p) => (m += p)); istek.on("end", () => coz(m ? JSON.parse(m) : {}));
});

const sunucu = http.createServer(async (istek, yanit) => {
  const yol = new URL(istek.url, "http://127.0.0.1").pathname;
  const gonder = (durum, govde) => {
    yanit.writeHead(durum, { "content-type": "application/json" });
    yanit.end(JSON.stringify(govde));
  };

  if (yol === "/olcumler") {
    if (!oturumAcik || istek.headers.authorization !== `Bearer ${gecerliErisim}`)
      return gonder(401, { kod: "kimlik_yok" });
    return gonder(200, { kayit: "o-114", surum });
  }

  if (yol === "/oturum/yenile") {
    const { yenileme } = await govdeyiOku(istek);
    if (kullanilmis.has(yenileme)) {          // ayni belirtec ikinci kez: aile iptal
      oturumAcik = false;
      return gonder(401, { kod: "yeniden_kullanim" });
    }
    if (!oturumAcik || yenileme !== gecerliYenileme)
      return gonder(401, { kod: "yenileme_gecersiz" });
    kullanilmis.add(yenileme);
    surum += 1; yenilemeSayaci += 1;
    gecerliErisim = `e${surum}`; gecerliYenileme = `y${surum}`;
    return gonder(200, { erisim: gecerliErisim, yenileme: gecerliYenileme });
  }

  if (yol === "/oturum/kapat") { oturumAcik = false; return gonder(204, {}); }
  gonder(404, { kod: "yol_yok" });
});
await new Promise((coz) => sunucu.listen(0, "127.0.0.1", coz));
const TABAN = `http://127.0.0.1:${sunucu.address().port}`;

// --- Istemci ----------------------------------------------------------------
const oturum = { erisim: "e0", yenileme: "y1", acik: true };   // erisim bilerek eskitildi
let ucustaki = null;

function yenile(kullanilanErisim) {
  // Baskasi zaten yeniledi: bu istek yeni belirtecle dogrudan tekrarlanir.
  if (kullanilanErisim !== oturum.erisim) return Promise.resolve(true);
  if (ucustaki) return ucustaki;

  ucustaki = (async () => {
    const yanit = await fetch(`${TABAN}/oturum/yenile`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ yenileme: oturum.yenileme }),
    });
    if (!yanit.ok) { oturum.acik = false; return false; }
    const govde = await yanit.json();
    oturum.erisim = govde.erisim;
    oturum.yenileme = govde.yenileme;         // donusum: eski belirtec artik gecersiz
    return true;
  })();
  ucustaki.finally(() => { ucustaki = null; });
  return ucustaki;
}

async function istek(ad) {
  if (!oturum.acik) return `${ad}: oturum kapalı`;
  const kullanilan = oturum.erisim;
  let yanit = await fetch(`${TABAN}/olcumler`,
    { headers: { authorization: `Bearer ${kullanilan}` } });
  if (yanit.status !== 401) return `${ad}: ${yanit.status}`;

  const yenilendi = await yenile(kullanilan);
  if (!yenilendi) return `${ad}: oturum düştü`;

  yanit = await fetch(`${TABAN}/olcumler`,
    { headers: { authorization: `Bearer ${oturum.erisim}` } });
  const govde = await yanit.json();
  return `${ad}: ${yanit.status} sürüm=${govde.surum ?? "-"}`;
}

// --- 1. Ayni anda dusen dort istek -----------------------------------------
const sonuclar = await Promise.all(["istasyon", "geçmiş", "özet", "uyarı"].map(istek));
sonuclar.forEach((s) => console.log("  " + s));
console.log("sunucudaki yenileme sayısı:", yenilemeSayaci);

// --- 2. Eski yenileme belirtecinin ikinci kez kullanilmasi ------------------
const eski = "y1";
const y = await fetch(`${TABAN}/oturum/yenile`, {
  method: "POST", headers: { "content-type": "application/json" },
  body: JSON.stringify({ yenileme: eski }),
});
console.log("\neski yenileme belirteci →", y.status, JSON.stringify(await y.json()));

// --- 3. Aile iptal edildikten sonra ----------------------------------------
oturum.acik = true;                            // istemci henuz bilmiyor
console.log("iptal sonrası istek        →", await istek("geçmiş"));
console.log("istemcinin oturum durumu   :", oturum.acik ? "açık" : "kapalı");

sunucu.close();
  istasyon: 200 sürüm=2
  geçmiş: 200 sürüm=2
  özet: 200 sürüm=2
  uyarı: 200 sürüm=2
sunucudaki yenileme sayısı: 1

eski yenileme belirteci → 401 {"kod":"yeniden_kullanim"}
iptal sonrası istek        → geçmiş: oturum düştü
istemcinin oturum durumu   : kapalı

Çıktının Okunması

Dört istek de başarıyla döndü ve sunucudaki yenileme sayacı bir gösterdi. Dört ayrı çağrı noktası tek bir yenilemeyi paylaştı; hiçbiri bir diğerinin varlığını bilmiyor.

Toplamayı sağlayan iki koruma var ve ikisi de gereklidir. Birincisi uçuştaki söz: bir yenileme sürerken gelen ikinci istek yenisini başlatmaz, sürene katılır. İkincisi kullanılan belirtecin karşılaştırılması: 401’i geç fark eden bir istek, yenileme çoktan bitmişse hiç yenileme istemez, yeni belirteçle doğrudan yinelenir. Yalnız birincisi yazıldığında, yenileme bittikten hemen sonra gelen bir 401 gereksiz bir ikinci yenileme başlatır.

surum=2 değeri, dört isteğin de yenilemeden sonraki belirteçle yanıtlandığını gösteriyor. Yineleme, yenileme sırasında değişmiş olan belirteci okumak zorundadır; isteğin başında yakalanan eski değeri kullanmak yinelemeyi de 401’e düşürürdü.

Dönüşüm ve Yeniden Kullanım Saptaması

Yenileme belirteci uzun ömürlüdür ve bu onu oturumun en değerli parçası yapar. İki düzenek riski daraltır.

Dönüşüm (rotation): her yenilemede yalnız erişim belirteci değil, yenileme belirteci de değişir. Kullanılmış olan bir daha kabul edilmez. Böylece bir yenileme belirtecinin kullanılabilir ömrü, bir sonraki yenilemeye kadardır.

Yeniden kullanım saptaması: kullanılmış bir yenileme belirteci ikinci kez gelirse, sunucu bunu tek bir istemcinin hatası saymaz. İki olasılık vardır — ya bir kopya dolaşımdadır ya da yenileme yanıtı istemciye ulaşmadan bağlantı kopmuştur. İkisini ayırt etmek mümkün olmadığı için sunucu güvenli tarafı seçer ve o oturuma bağlı bütün belirteçleri iptal eder. Çıktının ikinci bölümü bunu gösteriyor: y1 ikinci kez gönderildiğinde yanıt yeniden_kullanim oldu.

Üçüncü bölüm iptalin sonucunu gösteriyor. İstemci oturumun kapandığını bilmiyordu ve isteğini gönderdi; 401 aldı, yenilemeyi denedi, o da reddedildi ve istemci oturumu kapalı işaretledi. Yani oturumun bittiğini istemciye söyleyen şey yenilemenin başarısız olmasıdır.

Bu düzeneğin bir yan etkisi vardır: ağ kopması yüzünden kaçırılan bir yenileme yanıtı da oturumu düşürür. Kabul edilen bir ödünleşimdir; alternatifi, kullanılmış bir belirtecin bir süre daha geçerli sayılmasıdır ki bu, saptamanın değerini azaltır.

Döngü ve Kuyruk Tuzakları

Yenileme kodu iki tuzağa açıktır ve ikisi de üretimde görülür.

Sonsuz döngü. Yenileme isteğinin kendisi 401 döndürürse ve o istek de aynı yenileme düzeneğinden geçiyorsa, döngü kapanmaz. Kural: yenileme isteği kesinlikle yenileme düzeneğinden geçmez ve kendi 401’inde yeniden denenmez. Yukarıdaki kodda yenileme çağrısı istek işlevini kullanmıyor, doğrudan gönderiliyor.

Tek yineleme sınırı. Yenilendikten sonra yinelenen istek yine 401 alırsa ikinci bir yenileme denenmez. Bu durum, kullanıcının o kaynağa gerçekten yetkisi olmadığını gösterir; yetki hatasını kimlik hatası sanıp yenilemeye devam etmek, oturumu boş yere yıpratır.

Bir de kuyruk kararı vardır. Yenileme sürerken gelen yeni istekler iki biçimde ele alınabilir: eski belirteçle gönderilip 401 alarak kuyruğa katılmak, ya da yenileme bitene kadar hiç gönderilmemek. İkincisi gereksiz istek üretmez ama uygulamanın her isteği kimlik durumundan haberdar bir kapıdan geçirmesini gerektirir. Yukarıdaki kod birinciyi uyguluyor; dört isteğin dördü de gitti, 401 aldı, tek yenilemede toplandı.

Çıkış Akışı

Çıkış üç adımdan oluşur ve üçü de gereklidir.

Sunucuda geçersizleştirme. Çerez tabanlı bir düzende oturum kaydı silinir ve çerez boş bir değerle, geçmiş bir son kullanma tarihiyle yeniden yazılır. Belirteç tabanlı bir düzende yenileme belirteci iptal edilir. Yalnız istemci tarafını temizlemek çıkış değildir; belirtecin bir kopyası hâlâ geçerli kalır.

İstemci durumunun temizlenmesi. Bellekteki belirteç, kimlik durumu ve önbellekteki kullanıcıya özel veriler silinir. Sonuncusu sıkça atlanır: ölçüm geçmişi önbellekte kalırsa, aynı tarayıcıda oturum açan ikinci bir gözlemci bir önceki kullanıcının verisini bir an için görür. Veri Erişimi konusundaki normalleştirilmiş depo bu temizliğin hedefidir.

Sekmeler arası eşitleme. Aynı kullanıcı uygulamayı üç sekmede açmış olabilir. Bir sekmede yapılan çıkış diğerlerine bildirilmezse, o sekmeler oturum açıkmış gibi davranmaya devam eder ve ilk isteklerinde beklenmedik biçimde 401 alır. Bildirim, aynı kaynağın sekmeleri arasında ileti geçişiyle ya da paylaşılan depolamadaki bir değişikliğin izlenmesiyle yapılır; ikisi de Tarayıcı ve Web Platformu kursunda tanıtıldı. Aynı düzenek ters yönde de çalışır: bir sekmede açılan oturum diğerlerinde de açılmış sayılır.

Çıkışın kullanıcı tarafındaki karşılığı da düşünülmelidir. Yarım kalmış bir formun içeriği çıkışta silinir; kullanıcıya bunun sorulması, sessizce silmekten iyidir. Zorunlu çıkışta — oturum düştüğünde — kullanıcı giriş ekranına yönlendirilirken bulunduğu adres saklanır ve giriş sonrası oraya dönülür.

Özet

  • Yenilemenin üç tetikleyicisi vardır: 401 sonrası tepkisel, süre dolmadan önden ve sayfa açılışında sessiz oturum kurma. Önden yenileme tepkiselin yerine geçmez.
  • Eşzamanlı yetki hataları tek uçuşlu bir yenilemede toplanır; uçuştaki söz ile kullanılan belirtecin karşılaştırılması birlikte gerekir.
  • Yinelenen istek, yenileme sırasında değişmiş belirteci okumalıdır; isteğin başında yakalanan değer artık geçersizdir.
  • Yenileme belirteci her kullanımda dönüşür; kullanılmış bir belirtecin ikinci kez gelmesi oturumun tümünün iptaliyle sonuçlanır.
  • Yenileme isteği yenileme düzeneğinden geçmez ve yinelenen istek ikinci kez 401 alırsa yeniden yenilenmez.
  • Çıkış sunucuda geçersizleştirme, istemci durumu ile kullanıcıya özel önbelleğin temizlenmesi ve sekmeler arası eşitleme adımlarının üçünü birden gerektirir.

Sonraki Adım

Kimlik akışı tamamlandı: gözlemci tanınıyor, oturumu kendiliğinden sürüyor, çıkışı her yerde etkili oluyor. Ama tanınan gözlemcinin bir de dili var. Bu kurs boyunca üretilen her metin — hata iletileri, “oturum düştü” bildirimi, boş sonuç açıklamaları, tarih ve sayı biçimleri — tek bir dile göre yazıldı. İstasyonu Norveç’ten izleyen bir gözlemci için “−4,2” doğru biçim değildir, “2 alan düzeltilmeli” doğru cümle değildir ve arayüzün akış yönü bile farklı olabilir. Son ders bu uyarlamayı kurar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat