Yalnızca Notlarınız
Coddy'nin Node.js Journey'sinin Kimlik Doğrulama ve Güvenlik bölümünün bir parçası. Ders 18 / 24.
Oturum açmış olman kim olduğunu kanıtlar, bir notun sana ait olduğunu değil. GET /notes/:id, notu yalnızca id değerine göre bulur; bu nedenle oturum açmış ve ortak bir bağlantıdan ya da günlükten bir id edinmiş herkes bu notu okuyabilir. Sahipliği denetlemeye yetkilendirme denir.
Sahibi doğrudan sorguya ekle; böylece veritabanı başka birine ait belgeyi asla döndürmez. Bulunamadığında 403 değil, 404 yanıtı ver: 403, ziyaretçiye notun var olduğunu bildirir.
const order = await orders.findOne({
_id: new ObjectId(req.params.id),
customer: req.user.id,
});
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}Bir kullanıcının verilerine dokunan her sorguda bu filtre bulunmalıdır: okuma, güncelleme ve silme işlemlerinde.
Görev
KolayBu bölüm, 2. bölümdeki oturum açma konusuna geri dönüyor ve sunucu genişledi. Artık GET / adresinde bir sayfası, tek bir not için GET /notes/:id ve DELETE /notes/:id yolları ve bir yönetici hesabı var. İki /notes/:id yolunda bir açık bulunuyor: notu yalnızca id değerine göre buluyorlar. Bir id bilen oturum açmış herhangi bir kullanıcı, başka birinin notunu okuyabilir veya silebilir. Bu açığı kapat.
server.jsdosyasında,GET /notes/:idiçinde notu hem_idhem deownerdeğerlerine göre bul. Sahibi, oturum açmış kullanıcı olanreq.user.idolmalı.- Aynısını
DELETE /notes/:idiçinde yap; böylece hiç kimse sahibi olmadığı bir notu silemez. - Eşleşen bir not bulunmadığında her iki yol da zaten 404 durum koduyla
{"error": "Note not found"}yanıtını veriyor. Başkasının notu için de bu yanıtı kullan. 403 yanıtı, ziyaretçiye notun var olduğunu bildirirdi.
Deneyin: Çalıştır'a basın.
{"username": "ada", "password": "correct horse"}içeriğiylePOST /signup, ardındanPOST /loginisteği gönderin.{"text": "Safe code 4417"}içeriğiylePOST /notesisteği gönderin ve yanıttaki_iddeğerini kopyalayın. Bu id'nin ardındanGET /notes/gönderildiğinde not döner.{"username": "bob", "password": "correct horse"}içeriğiyle kaydolup oturum açın. Ada'nın id değerinin ardındanGET /notes/gönderin: durum kodu 404 ve yanıt{"error": "Note not found"}olur. Aynı id'nin ardındanDELETE /notes/gönderin: yine 404.- İlk adımdaki gövdeyi kullanarak yeniden ada olarak oturum açın. Notu hâlâ orada. Şimdi
DELETEisteği gönderin: durum kodu 204.
Kendin dene
import express from 'express';
import { ObjectId } from 'mongodb';
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());
// Tek admin hesabı. Parolası ortam değişkenlerinden alınır.
await users.insertOne({ username: 'admin', role: 'admin', passwordHash: await hashPassword(process.env.ADMIN_PASSWORD) });
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,
sameSite: 'lax',
secure: true,
maxAge: 24 * 60 * 60 * 1000,
});
res.json({ username: user.username });
});
// Oturum çerezinin ait olduğu kullanıcıyı bulur; bulamazsa 401 yanıtı verir.
async function requireUser(req, res, next) {
const { sid } = parseCookies(req.get('cookie'));
const session = sid && (await sessions.findOne({ _id: sid }));
const user = session && (await users.findOne({ _id: session.userId }));
if (!user) {
return res.status(401).json({ error: 'Log in first' });
}
req.user = { id: user._id.toHexString(), username: user.username, role: user.role };
next();
}
app.get('/me', requireUser, (req, res) => {
res.json({ username: req.user.username });
});
app.post('/logout', async (req, res) => {
const { sid } = parseCookies(req.get('cookie'));
if (sid) {
await sessions.deleteOne({ _id: sid });
}
res.clearCookie('sid');
res.status(204).end();
});
// Notlarınız bir sayfa olarak.
app.get('/', requireUser, async (req, res) => {
const list = await notes.find({ owner: req.user.id }).toArray();
const items = list.map((note) => `<li>${note.text}</li>`).join('');
res.send(`<!doctype html><title>Vault</title><h1>${req.user.username}'s notes</h1><ul>${items}</ul>`);
});
// Notlar: Her kullanıcı yalnızca kendi notlarını okur ve yazar.
app.get('/notes', requireUser, async (req, res) => {
res.json(await notes.find({ owner: req.user.id }).toArray());
});
app.post('/notes', requireUser, async (req, res) => {
const note = { owner: req.user.id, text: String(req.body.text ?? '') };
await notes.insertOne(note);
res.status(201).json(note);
});
app.get('/notes/:id', requireUser, async (req, res) => {
if (!ObjectId.isValid(req.params.id)) {
return res.status(404).json({ error: 'Note not found' });
}
const note = await notes.findOne({ _id: new ObjectId(req.params.id) });
if (!note) {
return res.status(404).json({ error: 'Note not found' });
}
res.json(note);
});
app.delete('/notes/:id', requireUser, async (req, res) => {
if (!ObjectId.isValid(req.params.id)) {
return res.status(404).json({ error: 'Note not found' });
}
const { deletedCount } = await notes.deleteOne({ _id: new ObjectId(req.params.id) });
if (!deletedCount) {
return res.status(404).json({ error: 'Note not found' });
}
res.status(204).end();
});
// Yöneticiler için: Kaç hesap olduğu.
app.get('/admin', requireUser, async (req, res) => {
res.json({ users: await users.countDocuments() });
});
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
1Şifreler
Baytlar ve Onaltılık MetinParola Anahtarı TüretmeYeni Bir TuzParolaları Asla SaklamayınAnahtar Baytlarını KarşılaştırmaParolayı DoğrulamaKayıt Kuralları4Sahiplik ve Güvenlik
Yalnızca NotlarınızYönetici RolleriKendi başına pratik yap: Express playground