Giriş Oturumları
Coddy'nin Node.js Journey'sinin Kimlik Doğrulama ve Güvenlik bölümünün bir parçası. Ders 10 / 24.
Giriş isteği 200 yanıtını verir, ancak sonraki istek kimin giriş yaptığını unutmuştur. Bir oturum bunu hatırlar. Giriş yapıldığında sunucu, kimsenin tahmin edemeyeceği bir kimlik oluşturur, bunun hangi kullanıcıya ait olduğunu kaydeder ve çerez olarak gönderir. Sonraki her istek kimliği geri getirir ve sunucu bu kimliği arar.
Kimlik rastgele olmalıdır. Kullanıcı adını tutan bir çerez, herkesin Cookie: sid=ada yazıp Ada olmasına imkân verirdi. randomBytes(24), kimsenin tahmin edemeyeceği 24 rastgele bayt, yani 192 bit üretir. toString('base64url') bunları 32 harf, rakam, - ve _ karakteri olarak yazar; bu karakterler çerezde güvenle kullanılabilir. Sayfadaki betiklerin okuyamaması için çerezin httpOnly özelliğini ayarlayın.
const cartId = randomBytes(24).toString('base64url');
await carts.insertOne({ _id: cartId, items: [] });
res.cookie('cart', cartId, { httpOnly: true });
// daha sonraki bir istekte
const cart = await carts.findOne({ _id: parseCookies(req.get('cookie')).cart });Görev
KolayBu adım, Remembering with Cookies ( server.js dâhil) içindeki proje dosyaların yerine, kurs için hazırlanmış özel notlar API projesini açar. Önceki çalışmaların kendi dersinde kalır. server.js dosyasını düzenlemeden önce sağlanan dosyaları oku. Bu kopyada özel notlar için kayıt ve giriş rotaları bulunur.
Giriş çalışıyor, ancak sonraki istekte kimin giriş yaptığı çoktan unutulmuş oluyor. Sunucunun bunu bir oturumla hatırlamasını sağla: sunucuda kaydedilen ve tarayıcıya çerez olarak gönderilen rastgele bir kimlik.
server.jsiçindenode:cryptomodülündenrandomBytesiçe aktar../db.jsmodülündensessionsöğesininotesveusersöğelerinin yanına içe aktar. Kilitlidb.jsdosyasında zaten birsessionskoleksiyonu var.POST /loginiçinde, parola doğrulandıktan sonra bir oturum kimliği oluştur: base64url metnine dönüştürülmüş 24 rastgele bayt. Bu,-ve_dâhil harf ve rakamlardan oluşan 32 karakterdir; örneğinoqjRxvxmXl_nRwAwOLxgZyI2sYq9hZtN. Her girişte yeni bir kimlik oluştur.- Oturumu,
sessionskoleksiyonuna iki alanı olan bir belge olarak kaydet:_id: oturum kimliği.userId: giriş yapan kullanıcının_iddeğeri.
- Kimliği tarayıcıya
sidadlı bir çerez olarak, sayfadaki betiklerin çerezi okuyamaması içinhttpOnlyseçeneğiyle gönder. Giriş yanıtı yine{"username": "ada"}olsun. GET /merotasını ekle. Bu rotasidçerezini okur, bu_iddeğerine sahip oturumu bulur ve ardından o oturumun kullanıcısını bulur.{"username": "ada"}yanıtını verir.sidçerezi yoksa veya bu kimliğe sahip bir oturum bulunmuyorsa, 401 durum kodunu ve{"error": "Log in first"}yanıtını verir.sid=adagibi bir çerezi elle yazan biri de bu 401 yanıtını almalıdır.
Deneyin: “Çalıştır” düğmesine basın. {"username": "ada", "password": "correct horse"} gövdesiyle POST /signup isteği gönderin. GET /me isteği gönderin: 401 durum kodu ve {"error": "Log in first"} yanıtı alın. Kayıt isteğiyle aynı gövdeyi kullanarak POST /login isteği gönderin: 200 durum kodu alın. Yanıtın Headers sekmesinde, noktaların yerine rastgele kimliğiniz gelecek şekilde Set-Cookie: sid=...; Path=/; HttpOnly görünür. GET /me isteğini yeniden gönderin. Tarayıcı çerezi geri gönderir, böylece {"username": "ada"} yanıtını alırsınız. Veri sekmesini açın: sessions koleksiyonunda bir belge bulunur; belgenin _id alanında kimliğiniz, userId alanında ise ada'nın _id değeri yer alır.
Kendin dene
import express from 'express';
import { parseCookies } from './cookies.js';
import { notes, users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
const app = express();
app.use(express.json());
app.post('/signup', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const password = String(req.body.password ?? '');
if (!username || password.length < 8) {
return res.status(400).json({ error: 'Pick a username and a password of at least 8 characters' });
}
if (await users.findOne({ username })) {
return res.status(409).json({ error: 'That username is taken' });
}
await users.insertOne({ username, passwordHash: await hashPassword(password) });
res.status(201).json({ username });
});
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
res.json({ username: user.username });
});
// Bu tarayıcının GET /visit isteğini kaç kez yaptığını sayar.
app.get('/visit', (req, res) => {
const cookies = parseCookies(req.get('cookie'));
const visits = Number(cookies.visits ?? 0) + 1;
res.cookie('visits', String(visits));
res.json({ visits });
});
// Notlar. Şimdilik herkes bunları okuyabilir ve yazabilir.
app.get('/notes', async (req, res) => {
res.json(await notes.find().toArray());
});
app.post('/notes', async (req, res) => {
const note = { text: String(req.body.text ?? '') };
await notes.insertOne(note);
res.status(201).json(note);
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
Kimlik Doğrulama ve Güvenlik bölümündeki tüm dersler
Kendi başına pratik yap: Express playground