Giriş Gerektiren Rotalar
Coddy'nin Node.js Journey'sinin Kimlik Doğrulama ve Güvenlik bölümünün bir parçası. Ders 11 / 24.
Her özel rota, GET /me üzerinden oturum sorgulamasına ihtiyaç duyar. Her rotaya kopyalarsan bir keresinde unutulur ve o rota herkese açık kalır. Bunun yerine bir ara katman yazılımı olarak bir kez yaz.
Ara katman yazılımı rotadan önce çalışır. req üzerine req.user gibi bir şey eklediğinde rota bunu oradan okur. Bir kullanıcının _id değeri bir ObjectId'dir; user._id.toHexString() bunu metin olarak verir. Ya bir yanıt gönderip işlemi durdurur ya da bulduğu şeyi req üzerine ekleyip next() çağırır. Bunu ihtiyaç duyan her rotaya, işleyiciden önce geçir:
function requireAdult(req, res, next) {
if (Number(req.query.age) < 18) {
return res.status(403).json({ error: 'Adults only' });
}
req.adult = true;
next();
}
app.get('/wines', requireAdult, (req, res) => res.json(wines));return önemlidir: o olmazsa rota da çalışır.
Görev
KolayNotlar hâlâ herkesin erişimine açık. Onları özel hâle getir: yalnızca giriş yapmış bir kullanıcı notları okuyabilir veya ekleyebilir ve her kullanıcı yalnızca kendi notlarını görür.
server.jsdosyasındarequireUser(req, res, next)ara katman işlevini yaz. Oturum arama işleminiGET /meiçinden buraya taşı:sidçerezini oku, oturumu bul, ardından oturumun kullanıcısını bul.- Böyle bir kullanıcı yoksa 401 durum koduyla
{"error": "Log in first"}yanıtını ver ve burada dur. - Aksi takdirde, kullanıcıyı iki alanı olan bir nesne olarak
req.userüzerinden isteğe ekle, ardındannext()çağrısını yap. Bu iki alanid(kullanıcının_iddeğerinin metin hâli) veusernamealanlarıdır. ObjectId'nintoHexString()yöntemi,"66f1c2a9e4b0a1b2c3d4e5f6"gibi metin hâlini verir.
- Böyle bir kullanıcı yoksa 401 durum koduyla
requireUserişlevini, her rotanın kendi işlevinden önceGET /me,GET /notesvePOST /notesrotalarına ekle. Bunun ardındanGET /me,req.userüzerinden okuduğu{"username": "ada"}yanıtını verir.POST /notesiçinde her notu,req.user.iddeğerini tutan birowneralanıyla kaydet. Yanıt yeni nottur:{"owner": "66f1c2a9e4b0a1b2c3d4e5f6", "text": "Door code 1234", "_id": "66f1c2b0e4b0a1b2c3d4e5f7"}GET /notesiçinde yalnızcaownerdeğeri giriş yapmış kullanıcıya ait olan notları döndür.
Deneyin: Çalıştır'a bas. GET /notes isteği gönder: 401 durum kodu ve {"error": "Log in first"} yanıtını al. {"username": "ada", "password": "correct horse"} içeriğiyle önce POST /signup, ardından POST /login isteği gönder. {"text": "Door code 1234"} içeriğiyle POST /notes isteği gönder: 201 durum kodu ve owner alanı da dahil olmak üzere notu al. GET /notes isteği gönder: bu tek notu içeren bir liste al. Şimdi {"username": "bob", "password": "correct horse"} içeriğiyle kaydol ve giriş yap. bob hiçbir şey yazmadığı için GET /notes, [] döndürür.
Kendin dene
import express from 'express';
import { randomBytes } from 'node:crypto';
import { parseCookies } from './cookies.js';
import { notes, sessions, 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' });
}
const sid = randomBytes(24).toString('base64url');
await sessions.insertOne({ _id: sid, userId: user._id, createdAt: new Date() });
res.cookie('sid', sid, { httpOnly: true });
res.json({ username: user.username });
});
app.get('/me', async (req, res) => {
const { sid } = parseCookies(req.get('cookie'));
const session = sid && (await sessions.findOne({ _id: sid }));
if (!session) {
return res.status(401).json({ error: 'Log in first' });
}
const user = await users.findOne({ _id: session.userId });
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');
});
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Kimlik Doğrulama ve Güvenlik bölümündeki tüm dersler
Kendi başına pratik yap: Express playground