Sessioni di accesso
Fa parte della sezione Autenticazione e sicurezza del percorso Node.js di Coddy. Lezione 10 di 24.
Il login risponde con 200, poi la richiesta successiva ha dimenticato chi ha effettuato l’accesso. Una sessione lo ricorda. Al momento dell’accesso, il server crea un ID impossibile da indovinare, memorizza a quale utente appartiene e lo invia come cookie. Ogni richiesta successiva rimanda l’ID e il server lo cerca.
L’ID deve essere casuale. Un cookie che contiene il nome utente permetterebbe a chiunque di digitare Cookie: sid=ada e diventare Ada. randomBytes(24) fornisce 24 byte casuali, 192 bit, impossibili da indovinare. toString('base64url') li scrive come 32 lettere, cifre, - e _, caratteri sicuri da usare in un cookie. Imposta il cookie httpOnly in modo che gli script nella pagina non possano leggerlo.
const cartId = randomBytes(24).toString('base64url');
await carts.insertOne({ _id: cartId, items: [] });
res.cookie('cart', cartId, { httpOnly: true });
// in una richiesta successiva
const cart = await carts.findOne({ _id: parseCookies(req.get('cookie')).cart });Sfida
FacileQuesto passaggio apre il progetto API per le note private predisposto per il corso, anziché i file del tuo progetto di Ricordare con i cookie (incluso server.js). Il lavoro precedente rimane nella sua lezione. Leggi i file forniti prima di modificare server.js. Questa copia contiene le route di registrazione e accesso per le note private.
L'accesso funziona, ma la richiesta successiva ha già dimenticato chi ha effettuato l'accesso. Fai in modo che il server ricordi l'utente tramite una sessione: un ID casuale salvato sul server e inviato al browser come cookie.
- In
server.js, importarandomBytesdanode:crypto. Importasessionsda./db.jsinsieme anoteseusers. Il file bloccatodb.jscontiene già una raccoltasessions. - In
POST /login, dopo aver verificato la password, crea un ID di sessione: 24 byte casuali convertiti in testo base64url. Sono 32 caratteri tra lettere, cifre,-e_, comeoqjRxvxmXl_nRwAwOLxgZyI2sYq9hZtN. Crea un nuovo ID a ogni accesso. - Salva la sessione in
sessionscome documento con due campi:_id: l'ID di sessione.userId: il_iddell'utente che ha effettuato l'accesso.
- Invia l'ID al browser come cookie denominato
sid, con l'opzionehttpOnly, in modo che gli script nella pagina non possano leggerlo. L'accesso continua a rispondere con{"username": "ada"}. - Aggiungi
GET /me. Legge il cookiesid, cerca la sessione con quell'_id, poi trova l'utente associato a quella sessione. Risponde con{"username": "ada"}. Se non c'è il cookiesid, o nessuna sessione ha quell'ID, risponde con lo stato 401 e{"error": "Log in first"}. Chi inserisce manualmente un cookie, comesid=ada, deve ricevere lo stato 401.
Prova: premi «Gioca». Invia POST /signup con {"username": "ada", "password": "correct horse"}. Invia GET /me: stato 401 e {"error": "Log in first"}. Invia POST /login con lo stesso corpo della registrazione: stato 200. La scheda Headers della risposta mostra Set-Cookie: sid=...; Path=/; HttpOnly, con il tuo ID casuale al posto dei puntini. Invia di nuovo GET /me. Il browser rimanda il cookie, quindi ricevi {"username": "ada"}. Apri Data: sessions contiene un documento, con il tuo ID come _id e l'_id di ada come userId.
Provalo tu
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 });
});
// Conta quante volte questo browser ha chiamato GET /visit.
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 });
});
// Note. Per ora chiunque può leggerle e scriverle.
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');
});
Tutte le lezioni di Autenticazione e sicurezza
2Sessioni e cookie
Ricordare con i cookieUna ricerca di sessioneSessioni di accessoRoute protette dall’accessoEsercitati da solo: Playground Express