Il 0% ha trovato utile questo documento (0 voti)
0 visualizzazioni73 pagine

08 Node Js e Express

Node.js è un runtime JavaScript asincrono e guidato da eventi che consente di costruire applicazioni web lato server. Utilizza un event loop per gestire operazioni I/O non bloccanti, permettendo di gestire migliaia di connessioni concorrenti con un singolo thread. I vantaggi includono un'architettura scalabile, un ecosistema ricco di librerie e la possibilità di sviluppare sia il frontend che il backend con lo stesso linguaggio.

Caricato da

Luca Valenziano
Copyright
© All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
0 visualizzazioni73 pagine

08 Node Js e Express

Node.js è un runtime JavaScript asincrono e guidato da eventi che consente di costruire applicazioni web lato server. Utilizza un event loop per gestire operazioni I/O non bloccanti, permettendo di gestire migliaia di connessioni concorrenti con un singolo thread. I vantaggi includono un'architettura scalabile, un ecosistema ricco di librerie e la possibilità di sviluppare sia il frontend che il backend con lo stesso linguaggio.

Caricato da

Luca Valenziano
Copyright
© All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd

Fondamenti del Web

Ingegneria del Software e Fondamenti Web

Corso di Laurea in Ingegneria


Informatica e dell’Automazione
Anno Accademico 2025/2026

Prof. Antonio Ferrara

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
8 [Link]
e Express
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Cos’è
[Link]
[Link] è un “runtime asincrono di JavaScript guidato da eventi"

• runtime JavaScript: [Link] è un programma scritto in C++ che legge ed esegue del codice JavaScript grazie all’engine V8
(lo stesso incluso in Chrome) senza necessità di un browser ed effettua compilazione Just in Time
Possiamo quindi spostarci su un server e costruire web app: JS diventa un linguaggio lato server!

• guidato da eventi: una volta lanciata, l'applicazione rimane in ascolto di specifici eventi e reagisce mediante callback –
[Link] lavora secondo il pattern listener/emitter

• asincrono: [Link] completa i task in maniera asincrona all'interno di un event loop eseguito su un unico thread, grazie a
primitive di I/O che evitano operazioni bloccanti passandole al kernel di sistema e riprendendo l'esecuzione al loro termine
Questo permette a [Link] di gestire migliaia di connessioni concorrenti con un singolo thread senza doverne gestire la
concorrenza

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Cos’è
[Link]
[Link] è un “runtime asincrono di JavaScript guidato da eventi"

Il modello di [Link] è in contrasto con la maggior parte dei modelli di concorrenza odierni, in cui per la gestione delle
connessioni vengono impiegati i thread del sistema operativo
Un'applicazione [Link], invece, viene eseguita in un singolo processo, senza la creazione di un nuovo thread per ogni
richiesta di rete

La strategia di [Link] evita deadlock, in quanto quasi nessuna funzione di [Link] effettua operazioni I/O bloccanti
Fornisce, invece, un insieme di primitive di I/O asincrone (così come le librerie di [Link] sono generalmente scritte
utilizzando paradigmi non bloccanti)
In questo modo, quando è necessario effettuare un'operazione I/O come leggere dalla rete, accedere a un DB o al file
system, anziché bloccare il thread e sprecare CPU, il task viene eseguito in maniera asincrona e [Link] riprende le
operazioni quando ottiene una risposta

Queste caratteristiche permettono a [Link] di gestire migliaia di connessioni concorrenti su un singolo server senza dover
gestire la concorrenza di thread

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Perché
[Link]
Quali sono i vantaggi di [Link]?

• Permette di realizzare un’architettura event-driven che permette di ottimizzare la scalabilità (specie di applicazioni di rete)
grazie al fatto che le operazioni I/O (la maggior parte eseguite dai servizi web) possono essere autonomamente eseguite
all’interno di thread in background e le richieste concorrenti possono essere agevolmente gestite

• Permette di evitare il “context shift” fra linguaggi per lo sviluppo di frontend e backend

• Consente in diversi modi di realizzare strategie di caching delle richieste

• È inserito in un ecosistema ricco con oltre 1,3 milioni di librerie open source, documentate e supportate

• È portabile e multi-piattaforma

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Cos’è
[Link]

const x = 15; [Link]();


x++;

[Link] utilizza l’engine V8 per interpretare ed eseguire codice JavaScript


Inoltre, include il supporto alle [Link] API (analogia con le Web API), un insieme di librerie che utilizzano servizi del sistema
operativo per astrarre funzionalità complesse

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
L’event loop
di [Link]
Una volta eseguito lo script di input, [Link] entra – senza alcuna chiamata bloccante – all'interno di un event loop
L’event loop è ciò che permette a [Link] di effettuare operazioni I/O non bloccanti nonostante JavaScript sia single-thread,
chiedendo quando possibile al kernel di sistema di effettuare queste operazioni
La maggior parte dei kernel moderni è infatti multi-thread, e possono quindi gestire operazioni multiple in background
Quando una di queste operazioni viene completata, il kernel notifica [Link] affinché la callback appropriata venga aggiunta
alla poll queue per essere poi eseguita
Durante la fase di poll, se la coda è non vuota l’event loop
eseguirà le callback in maniera sincrona, fino a svuotarla
Se la coda è vuota, rimane in attesa che venga aggiunta una
nuova callback alla coda
Tuttavia, se sono state schedulate istruzioni del tipo
setImmediate(), esse vengono immediatamente
eseguite mediante la fase di check
Subito dopo, vengono eseguite le callback specificate nei
timer con setTimeout()
(Ricordiamo che i timer specificano la soglia minima dopo
cui una callback deve essere eseguita)

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Installazione e
configurazione
[Link] è platform-independent
È possibile gestire più installazioni di [Link] mediante Node Version Manager (NVM)

nvm list
nvm ls-remote
nvm install 9.3.0
node -v

L'installazione di [Link] rende disponibile anche npm per la gestione di librerie esterne (simile a pip in Python)

Con il comando node è possibile avvisare la versione REPL (Read-Evaluate-Print-Loop) di [Link]


nella shell prova i comandi .load, .save, .editor e .exit

Alternativamente, è possibile interpretare il codice JS all'interno del file [Link] con il comando node [Link] o con
node mycode

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Modularità e
Un classico comando
che è possibile
definire in [Link]
on è start che

creazione package
sarà richiamabile con
npm run
start o con npm st
art
Per configurarlo è po
ssibile inserire
nell’elenco degli scrip
t di
[Link] ad esem
Per la gestione dei moduli [Link] utilizza la strategia di CommonJS pio la voce
“start”: “node i
Diversi moduli possono essere organizzati in pacchetti gestiti mediante Node Package Manager [Link]”

(npm) Un pacchetto utile da


installare è
N.B. exports è una scorciatoia per nodemon, che perme
tte di avviare
uno script in modo ch
[Link] = ["a", "b", "c"] [Link], un oggetto cui poter e si riavvii
automaticamente og
ni volta che si
aggiungere proprietà da esportare osserva una modifica
nel codice
Quindi lo script può e
ssere avviato
const http = require("http"); con nodemon index
.js (che può
const messages = require("./messages"); essere anche configu
rato come
comando di start)

npm init npm è il gestore di pacchetti di default per [Link]


npm install <package> [—-save] Permette di installare e definire dipendenze, così come di creare nuovi pacchetti
npm run <comando> ed eseguirli
Crea, nella root di un pacchetto, un file [Link] che specifica tutte le sue
dipendenze e gli script che vengono eseguiti con npm run <comando>

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Il modulo
process
Il modulo process (facente parte del core di [Link]) fornisce una serie di metodi e proprietà utili per la gestione del
processo in esecuzione
Esso non necessita di essere importato, in quanto è automaticamente disponibile

[Link]()
È un modo programmatico per terminare l’esecuzione del processo Come impostare una
variabile d’ambiente?
USER_ID=239482 U
SER_KEY=foobar
[Link].VAR_NAME node [Link] quan
do lanciamo
l’applicazione, oppure
Permette di ottenere la variabile d’ambiente VAR_NAME mediante
require(‘dotenv'
).config(); se le
variabili sono imposta
te in un file .env nella
[Link] root del progetto
Permette di ottenere in un array gli argomenti passati allo script
I primi due elementi dell’array sono il path di [Link] e il path dello script

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Il primo
web server
const port = 3000;
const http = require("http");
const httpStatus = require("http-status-codes");
const app = [Link]((req, res) => {
La porta 3000 è
[Link]("Received a request!");
solitamente utilizzata
[Link]([Link], { per i
server di sviluppo
"Content-Type": "text/html"
});
[Link]("<h1>Hello, World!</h1>");
[Link]();
[Link](“Risposta inviata!”);
})

[Link](port, () => {
[Link](`The server has started listening on port ${port}`)
});

http è una libreria standard che introduce un supporto first-class per il networking

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Il primo
web server
Trasformiamo il codice precedente in maniera tale da realizzare un event handler per l’evento request sull’oggetto app
Notiamo che la sintassi per realizzare un event handler in [Link] è [Link](“evento”, callback)
const app = [Link]();
[Link](“request”, (req, res) => {
[Link]([Link], {
Un oggetto di tipo
"Content-Type": "text/html"
EventEmitter disp
}); one
del metodo emit per
[Link]("<h1>Hello, World!</h1>"); generare un evento e
[Link](“Risposta inviata!”); realizzare quindi il pa
ttern
}) listener/emitter

[Link](port, () => {
[Link](`The server has started listening on port ${port}`)
});

L’oggetto di risposta è un’istanza di [Link], i cui metodi fondamentali sono writeHead(status,


headers), setHeader('headername', value), write() per inviare buffered data e end([data])

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Il primo
web server La classe Buffer perm
di manipolare dati bin
ette
ari
come un array di byte
L’oggetto della richiesta è una istanza di [Link] È utilizzata, ad esemp
io,
Proprietà interessanti di questo oggetto permettono di analizzare il messaggio di richiesta come risposta di molt
i
metodi di lettura dei
file
[Link] [Link] [Link]

[Link]("request", (req, res) => {


Questo oggetto estende la classe [Link], const body = [];
ovvero la richiesta HTTP arriva al server come stream di [Link]("data", bodyData => {
dati [Link](bodyData);
Pertanto per leggere il corpo di una richiesta sarà });
necessario mettersi in ascolto degli eventi data e end [Link]("end", () => {
body = [Link](body).toString();
per la ricezione, rispettivamente, di nuovi dati e del
[Link](body);
segnale di fine dati
});
Ad esempio, spesso il corpo di una richiesta POST giunge });
al server in chunk, pertanto essa potrà essere gestita
come nell’esempio a destra Gestione del corpo di una richiesta POST

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Routing
in [Link]
Con il termine routing ci riferiamo alla determinazione di come
un’applicazione risponde ad una particolare richiesta ad un certo
endpoint (URL) e per un certo metodo
Con il seguente esempio, è possibile richiamare file .html in views/
mediante il loro nome dopo l’hostname
const fs = require(‘fs’);
const getViewUrl = (url) => `views${url}.html`;

[Link]((req, res) => {


[Link](getViewUrl([Link]), (error, data) => {
if (error) {
[Link](404);
} else { Il modulo fs fornisce una serie di funzionalità per
[Link](200, accedere e interagire con il file system
{“Content-Type": "text/html"});
Curiosità: la libreria bluebird permette di
[Link](data);
“promisificare” un oggetto
}
var Promise = require('bluebird');
[Link](); var fs = [Link](require('fs'));
});
};

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Esercizi
Esercizio 8.1

Immaginiamo di organizzare le directory del nostro progetto


come nello schema a destra

Supponiamo, quindi, di voler scrivere delle route per servire le


views HTML oppure gli asset pubblici in base all’estensione del
file richiesto (.jpg, .css, o .js)

In particolare, creare una funzione per la lettura dei file e invio


del file ritrovato o di un errore (la funzione dovrà quindi prendere
due parametri: il path del file e l’oggetto di risposta)

Ricorda che ciascun tipo di richiesta dovrà prevedere l’invio di un


header diverso per il content type

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Routing
in [Link]
Immaginiamo di realizzare un modulo [Link] (a sinistra) che ci permetta di registrare una serie di route e relative azioni
all’interno di un dizionario e che si occupi poi di gestirle

routes = {'GET': {}, 'POST': {}}; const http = require("http"),


router = require("./router");
[Link] = (req, res) => {
try { [Link]("/[Link]", (req, res) => {
if (routes[[Link]][[Link]]) { [Link](200, htmlContentType);
routes[[Link]][[Link]](req, res); customReadFile("views/[Link]", res);
} else { });
[Link](404);
} [Link]("/", (req, res) => {
} catch (ex) { [Link](200, plainTextContentType);
[Link](ex); [Link]("POSTED");
} });
}
[Link]([Link]).listen(3000);
[Link] = (url, action) =>
routes['GET'][url] = action;
[Link] = (url, action) =>
routes['POST'][url] = action;

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Framework
per [Link]
Come abbiamo visto, le API di [Link] risultano essere a basso livello e ciò rende difficoltoso eseguire operazioni più
complesse di routing o di elaborazione di richieste e risposte

A tale proposito, con il tempo sono stati sviluppati framework di più alto livello per [Link] che permettono di velocizzare
e semplificare lo sviluppo di applicazioni web
I web framework permettono, infatti, di superare alcune problematiche comuni di sviluppo come metodi e moduli per
semplificare la gestione di richieste con diversi metodi HTTP, il serving di contenuti statici e dinamici mediante template, la
connessione di database, etc.

Express è il framework più utilizzato nella comunità di [Link] ed offre:


• Gestori per le richieste effettuate con diversi metodi HTTP a differenti URL (routing)
• Integrazione con rendering engine per la realizzazione di viste mediante template
• Inserimento di middleware per l’elaborazione addizionale di richieste in qualunque momento della pipeline per la loro
gestione

Express è abbastanza minimalista, ma gli sviluppatori hanno creato middleware compatibili per la gestione di qualunque
problema di sviluppo (cookie, sessioni, login, parametri di URL, dati da POST, header di sicurezza, …)
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
La prima web app
con Express
const express = require('express');
[Link]() [Link] [Link]()
const app = express();
const port = 3000;
[Link]() [Link] [Link]()
[Link]('/', (req, res) => {
[Link]('Hello World!');
[Link]() [Link] [Link]()
});

[Link](port, () => { [Link]() [Link]


[Link](`App listening
on port ${port}`);
}); [Link]()

Notiamo che il metodo send() sull’oggetto di risposta si comporta in maniera simile al write() del modulo http, i cui
metodi sono ancora supportati da Express
Ricordiamo che write() deve essere però accompagnato da end()

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Routing
in Express
Catena di route

[Link]('/book')
.get((req, res) => {
[Link]('Get a random book’);
})
.post((req, res) => {
[Link]('Add a book’);
})
.put((req, res) => {
[Link]('Update the book’);
});

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Routing
in Express
All’interno dei metodi di routing è possibile specificare percorsi espliciti, o basati su pattern o basati su espressioni regolari, o
basati su parametri

Percorsi espliciti Pattern matching Espressioni regolari

[Link](‘/about', [Link](‘/ab(cd)?e', [Link](/.*fly$/,


(req, res) => { (req, res) => { (req, res) => {
[Link](…); [Link](…); [Link](‘/.*fly$/');
}); }); })

Percorsi basati su parametri


I percorsi basati su param
etri sono necessari per
[Link](‘/users/:userId/books/:bookId', realizzare architetture basa
te su REST
(req, res) => { Per controllare con precisio
ne il parametro è possibile
[Link]([Link]); affiancarlo ad una espress
ione regolare, ad
esempio :bookId(\d+)
});
// Request URL: [Link]
// [Link]: { "userId": "34", "bookId": "8989" }

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso di
middleware
Dopo la ricezione di una richiesta, Express può eseguire una serie di chiamate a funzioni
middleware intermedie, ad esempio prima che essa venga passata alla logica applicativa
dell’applicazione
Queste funzioni hanno accesso all’oggetto richiesta, all’oggetto risposta e alla funzione
middleware successiva del ciclo di richiesta/risposta (a cui ci si riferisce normalmente con next)

Le funzioni di routing stesse sono dei middleware

Le funzioni middleware possono eseguire diversi task:


• Eseguire codice generico
• Modificare gli oggetti di richiesta e di risposta
• Terminare il ciclo di richiesta/risposta
• Chiamare il middleware successivo nello stack

Se un middleware non termina il ciclo di richiesta/risposta, esso deve necessariamente


chiamare un middleware successivo, altrimenti la richiesta rimarrà pendente

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso di
middleware
[Link]()

È possibile agganciare un middleware (o un substack di


middleware) a un’istanza app dell’applicazione utilizzando
[Link]()

Questo metodo è fondamentale per attivare middleware a livello


di app oppure su percorsi e sottopercorsi

Ad esempio
[Link](callback)
esegue callback per richieste su qualunque percorso

[Link]('/users', callback)
permette di eseguire callback sul percorso /users e su tutti i
sottopercorsi come /users/tizio e
/users/tizio/photos
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso di
middleware [Link](‘/users’, myLogger);
[Link]('/', callback1);
[Link](‘/users/:userId’, callback2)
[Link]()
[Link](3000);

const myLogger = function (req, res, next) { In questo esempio:


[Link](‘LOGGED'); • una GET a /users passerà solo dal middleware
next();
myLogger
};
• una GET a /users/tizio passerà da myLogger e
[Link](myLogger); callback2
• una GET a /users/tizio/photos (o altri
[Link]('/', (req, res) => {
[Link]('Hello World!’);
sottopercorsi) passerà solo da myLogger
}); • Una GET a / passerà solo da callback1
• Una GET ad altri percorsi non sarà gestita
[Link](3000);

Ricorda che myLogger deve necessariamente chiamare il


L’ordine di caricamento delle funzioni middleware è
middleware successivo, altrimenti la richiesta rimarrà
importante: le funzioni caricate prima sono eseguite prima
pendente!

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso di
middleware
[Link]('/user/:id', (req, res, next) => {
// se l’ID dell’utente è 0 vai al prossimo route
if ([Link] === '0') next('route')
[Link]('/user/:id', (req, res, next) => {
// altrimenti continua con questo stack
[Link]('ID:', [Link]);
else next()
next();
}, (req, res, next) => {
}, (req, res, next) => {
// send a regular response
[Link]('User Info’);
[Link]('regular')
});
})
// il middleware seguente non verrà mai chiamato
// middleware per una risposta speciale
[Link]('/user/:id', (req, res, next) => {
[Link]('/user/:id', (req, res, next) => {
[Link]([Link]);
[Link]('special')
});
})
eren ze fra ap p. us e( ) e ap p. all()?
D iff
e che
In questo esempio vediamo un middleware substack che Il primo funziona per tutte le rout
ta,
estendono la base route specifica
esegue due callback, ed un middleware che non sarà mai te monta
mentre il secondo semplicemen
utilizzato in quanto il middleware precedente termina il ciclo un middleware per tutti i metod
i su uno
di richiesta/risposta specifico percorso

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso di
middleware
[Link]

[Link] permette di creare un router in maniera modulare che fornisca un sistema di routing e di middleware
completo (mini-app)

const express = require('express') const express = require('express')


const router = [Link]() const router = [Link]()

[Link]('/', (req, res) => { [Link]('/', (req, res) => {


[Link]('Birds home page') [Link]('Birds home page')
}) })
[Link]('/about', (req, res) => { [Link](‘/:id’, (req, res) => {
[Link]('About birds') [Link](`About user ${[Link]}`)
}) })

[Link] = router [Link] [Link] = router [Link]

const birds = require(‘./birds’)


I router vengono quindi visti come middleware che
const users = require(‘./users’)
[Link]('/birds', birds) possono essere “attivati” mediante [Link] sul relativo
[Link]('/users', users) [Link] percorso

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso di
middleware
In molti casi può essere utile utilizzare middleware built-in come [Link],
[Link] e [Link] oppure middleware esterni come cookie-parser

[Link]( var options = {


[Link]({ dotfiles: 'ignore',
extended: false etag: false,
}) extensions: ['htm', 'html'],
); index: false,
maxAge: '1d',
[Link]([Link]()); redirect: false,
setHeaders: function (res, path, stat) {
[Link]('x-timestamp', [Link]())
}
}

[Link]([Link]('public', options))

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso del
pattern MVC
La strategia principale per utilizzare il pattern MVC all’interno delle nostre web app Express è quella di spostare le funzioni
di callback all’interno di moduli separati che riflettono gli scopi di tali funzioni
Nell’esempio a sinistra, le definizioni delle callback invocate dalle route per la gestione degli account utente svolgono il
ruolo di controller e possono finire in un modulo [Link] all’interno della cartella controllers/

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Uso del
pattern MVC
Per quanto riguarda la realizzazione delle view, Express permette di utilizzare template engine come Pug, Mustache e EJS
Con i template engine è possibile realizzare template di view con variabili popolate dinamicamente e che vengono trasformati
in HTML da inviare ai client

EJS

Il template engine EJS può essere installato con npm install ejs ed il suo utilizzo può essere dichiarato con
[Link]("view engine", "ejs")

<% let x = 2; %> [Link] = function (req, res) {


<h1>Hello, <%= theName %>!</h1> [Link]('people', {theName: [Link]});
}
views/[Link] controllers/[Link]

All’interno dei simboli <% %> è possibile Il controller si occupa di fare il rendering della view mediante il
scrivere codice JavaScript metodo render sulla risposta, a cui viene passato il nome della view
Mediante <%= %> è possibile stampare il (anche senza estensione) e un oggetto contenente eventuali parametri/
valore di una variabile variabili

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Esercizi
Esercizio 8.2

Realizzare un’applicazione web per un’università utilizzando il pattern MVC e realizzando le viste mediante EJS
In particolare, realizzare una pagina che mostri i diversi corsi offerti dall’ateneo inseriti all’interno di un array, insieme ai
loro metadati
var courses = [
{
name: "Ingegneria Informatica e dell’Automazione",
type: “Corso di Laurea”, Nota bene!
imagePath: … Le viste possono accedere alle
},
{ proprietà dell’oggetto [Link]
name: "Ingegneria Informatica", come se fossero variabili locali
type: “Corso di Laurea Magistrale”, Ad esempio, settando
imagePath: … [Link] = “Prova”
},
{ potremo accedere direttamente alla
name: "Ingegneria Civile e Ambientale", variabile name all’interno della vista
type: “Corso di Laurea”, senza dover passare alcun oggetto
imagePath: …
}
];

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Il database
MongoDB
MongoDB è un database open-source non relazionale che organizza i record come documenti BSON (JSON-like binari)
All’interno di un database, è possibile inserire una o più collezioni, corrispondenti ai modelli dei nostri dati
All’interno di ciascuna collezione è possibile inserire (e ritrovare) tutti i documenti relativi ad uno specifico modello
Ogni documento è un oggetto che consente di memorizzare proprietà mediante il pattern chiave-valore

{ Un modello dei dati di questo tipo è detto embedded, in


_id: <ObjectId1>, quanto permette di incorporare dati correlati all’interno dello
username: “123xyz”,
stesso documento (modelli denormalizzati)
contact: {
L’uso di documenti embedded permette di ridurre il numero di
phone: “1234567890”,
email: “xyz@[Link]”, query e di attività I/O migliorando le performance
},
access: { MongoDB dispone, inoltre, di facility per la gestione di
level: 5, Ecco alcuni pattern per la repliche e nodi ridondanti, migliorando la disponibilità del
group: “dev” modellazione di relazioni database
} fra documenti embedded Allo stesso modo, supporta di default lo sharding sui nodi di
}
un cluster, garantendo la scalabilità orizzontale

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Il database
MongoDB
Il database MongoDB può essere installato in locale, oppure è possibile ottenere gratuitamente un’istanza multi-cloud
con MongoDB Atlas

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Il database
MongoDB
MongoDB fornisce il supporto alle operazioni CRUD sui documenti

[Link]({ [Link]()
name: "Ingegneria dell’Automazione”, [Link]({type: “Corso di Laurea”})
type: “Corso di Laurea Magistrale”, [Link]({type: {$in: [“Master”, “Corso di
students: 300 Laurea Magistrale”]}}
}) [Link]({status: “active", $or:
[Link]([…]) [{students: {$lt: 30}}, {other: /^p/}]})

[Link](
[Link]({…}) {students: {$lt: 10}},
[Link]({…}) {$set: {status: “deactivated"}}
)

Per garantire l’unicità dei documenti, MongoDB utilizza una classe ObjectId che permette di rappresentare univocamente
ogni documento nel database
Inoltre MongoDB supporta l’uso di indici, ovvero particolari strutture dati (B-tree) che permettono di memorizzare il valore di
uno o più specifici campi, per poter reperire documenti in maniera più efficiente mediante l’attraversamento dell’albero

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB
con Mongoose
La libreria Mongoose è un object-document mapper che permette di mappare oggetti JavaScript con i documenti di
MongoDB, consentendo di costruire modelli da utilizzare nel pattern MVC in termini di oggetti

const mongoose = require("mongoose");


[Link](“mongodb://localhost:27017/recipe_db", {useNewUrlParser: true});
const db = [Link];

[Link]("open", () => { [Link](“Successfully connected to MongoDB using Mongoose!”); });

I modelli in Mongoose sono realizzati mediante schemi


Ogni schema è una mappa verso una collezione MongoDB e definisce la forma dei documenti in tale collezione
const courseSchema = [Link]({
name: String,
type: String,
image: String
});
const Course = [Link]("Course", courseSchema);

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB
con Mongoose
All’interno di uno schema di Mongoose è possibile configurare ciascuna proprietà mediante uno SchemaType, che specifica
il tipo che una proprietà deve avere e quali valori sono validi
const breakfastSchema = [Link]({
• String
eggs: {
type: Number, • Number Ciascun tipo di dato
min: [6, 'Too few eggs'], • Date possiede dei validatori
max: 12 • Buffer propri: approfondisci qui
}, • Boolean
bacon: { • [Link]
type: Number,
required: [true, 'Why no bacon?']
• [Link]
}, • [Link].Decimal128
drink: { • Map
type: String, • [<type>] (array)
enum: {
values: ['Coffee', 'Tea'],
message: '{VALUE} is not supported' I validatori sono regole da poter applicare alle proprietà dei
} modelli per impedire di salvare documenti non validi
required: function() {
return [Link] > 3; La realizzazione di indici in uno schema Mongoose è
} possibile mediante index: true, mentre un indice
}
}); univoco è realizzabile come unique: true

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB
con Mongoose
È possibile aggiungere dei middleware sugli schemi (anche detti pre e post hook) per eseguire delle operazioni prima/dopo
che certe operazioni vengano eseguite
Il middleware andrà inserito subito dopo la definizione dello schema e prima della registrazione del modello

const schema = new Schema(..);


[Link]('save', function(next) { Dai un’occhiata ai diversi
// do stuff middleware che è possibile
next(); utilizzare
});

I middleware sono utili per la realizzazione di plugin, ovvero funzionalità preconfezionate per aggiungere funzionalità agli
schemi

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB
con Mongoose
Una volta realizzato un modello, esso può essere utilizzato all’interno dei controller per effettuare operazioni CRUD asincrone
Ad esempio, un controller come il seguente permette di agire all’interno della collezione courses del database in cui stiamo
lavorando (nome lowercase plurale)

const Course = require('../models/course'); L’utilizzo di [Link]


permette di creare un nuovo
[Link] = (req, res, next) => { documento e salvarlo in un
[Link]({})
.then(data => [Link]('courses', { courses: data })) unico comando
.catch(error => [Link](error));
};

[Link] = (req, res, next) => { [Link] = (req, res, next) => {
const newCourse = new Course({ [Link]({
name: 'Ingegneria delle Telecomunicazioni', name: 'Ingegneria delle Telecomunicazioni',
type: 'Corso di Laurea Magistrale', type: 'Corso di Laurea Magistrale',
}); })
[Link]() .then(savedDocument => [Link]('Corso inserito'))
.then(savedDocument => [Link]('Corso inserito')) .catch(error => [Link](error));
.catch(error => [Link](error)); };
};

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB
con Mongoose
Mongoose fornisce un insieme di metodi per effettuare query sui documenti

.find(…)
.findOne(…)
.findById(…)
.findByIdAndUpdate(…) Esplora tutti i metodi per le
.findByIdAndRemove(…) query e le API offerte
.distinct(…)
.delete(…)
.deleteOne(…)

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Associazione one-to-one embedded

Gestione del DB const profileSchema = [Link]({


birthday: Date,

con Mongoose })

const userSchema = [Link]({


…,
Come detto precedentemente, MongoDB è un database basato su profile: profileSchema
documenti, pertanto non esistono tabelle e join })
Le associazioni fra modelli possono essere fatte in maniera oppure
embedded (sottodocumenti) oppure mediante riferimento
const userSchema = [Link]({
…,
profile: {
One-to-one One-to-many Many-to-many birthday: Date,

}
})

Associazione one-to-one mediante reference


const userSchema = [Link]({
…,
profile: {
type: [Link],
ref: "Profile"
}
});
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Associazione one-to-many embedded

Gestione del DB const postSchema = [Link]({

con Mongoose
text: String,
imageUrl: String
})

const userSchema = [Link]({


Come detto precedentemente, MongoDB è un database basato su …,
posts: [postSchema]
documenti, pertanto non esistono tabelle e join })
Le associazioni fra modelli possono essere fatte in maniera
embedded (sottodocumenti) oppure mediante riferimento oppure

const userSchema = [Link]({


…,
One-to-one One-to-many Many-to-many posts: [{
text: String,
imageUrl: String
}]
})

La strategia di embedding nelle relazioni one-to-many


(ma anche one-to-one) è da preferirsi per ridurre il
numero di operazioni di lettura al database quando,
generalmente, intendiamo ritrovare in una singola
lettura il documento principale e i sottodocumenti

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Preferiamo una strategia di reference quando vogliamo

Gestione del DB anche lavorare in maniera specifica sui modelli figli, ad


es. se vogliamo trovare tutti i post che contengono una

con Mongoose certa parola chiave (se i post fossero embedded negli
utenti dovrei prima leggere tutti gli utenti)
Avere bisogno di accedere a un oggetto in sé è una
Come detto precedentemente, MongoDB è un database basato su buona ragione per non renderlo embedded
documenti, pertanto non esistono tabelle e join
Le associazioni fra modelli possono essere fatte in
t i pmaniera
ol o g ie di Associazione one-to-many mediante reference (padre ref. figli)
ed ue
Quale dell
embedded (sottodocumenti) oppure mediantee p r e fe rriferimento
ire? const userSchema = [Link]({
referen c f igli
l'a r ra y d e i …,
ia m o c h e
Se pens o (t a n t o d a posts: [{
r e s ce r e t ropp
possa c re fe r i a mo type: [Link],
One-to-one One-to-many dere")Many-to-many, allor a p
"espl o d re ref: "Post"
n o il p a
h e re f e re nzia }]
figli c
});

Associazione one-to-many mediante reference (figli ref. padre)


const postSchema = [Link]({
…,
user: {
type: [Link],
ref: "User"
}
});

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB Associazione many-to-many mediante reference

con Mongoose Come one-to-many, inserendo la reference delle istanze


di uno dei due modelli nell’altro: ad esempio, l’utente
conserva una lista dei corsi a cui è iscritto
Come detto precedentemente, MongoDB è un database basato su
const userSchema = [Link]({
documenti, pertanto non esistono tabelle e join …,
Le associazioni fra modelli possono essere fatte in maniera courses: [{
type: [Link],
embedded (sottodocumenti) oppure mediante riferimento
ref: "Course"
}]
});
One-to-one One-to-many Many-to-many
oppure

const courseSchema = [Link]({


…,
enrolledUsers: [{
type: [Link],
ref: "User"
}]
});

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB
con Mongoose
Associare un documento a un altro documento è possibile direttamente lavorando come oggetti JavaScript
Ad esempio, è possibile aggiungere un post all’elenco dei post di un utente mediante push sull’array
const testUser;
[Link]({name: “Mario”})
.then(result => testUser = result)
.then(() => [Link]({…}))
.then(createdPost => {
[Link](createdPost);
[Link]();
});

Inoltre, il metodo populate di un modello permette di popolare (ovvero espandere) un documento rispetto a un
particolare modello associato
const testUser;
[Link]({name: “Mario”}).then(result => testUser = result);

[Link](testUser, “posts”).then(result => [Link](result));

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Esercizi
Esercizio 8.3

Realizzare un’applicazione web per un’università che permetta di gestire gli studenti e i corsi di laurea
Realizzare le seguenti funzionalità:
• Visualizzazione di tutti gli studenti
• Inserimento di un nuovo studente mediante form
• Visualizzazione di tutti i corsi di laurea
• Inserimento di un nuovo corso di laurea mediante form
• Iscrizione di uno studente ad un corso di laurea

Nota che è possibile realizzare un controller che faccia il redirect verso una view (ad esempio, dopo aver inserito un
nuovo studente) nella maniera seguente, avendo settato [Link] nel controller precedente

redirectView: (req, res, next) => {


let redirectPath = [Link];
if (redirectPath) [Link](redirectPath);
else next();
}

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Gestione del DB
con Mongoose
Finora abbiamo realizzato operazioni CR, ovvero di Create e Read
Come detto, nel linguaggio dei metodi HTTP per le operazioni CRUD (e delle REST API) le operazioni di Update e Delete
corrispondono rispettivamente ai metodi HTTP PUT e DELETE

Ricordiamoci, tuttavia, che HTML supporta solo i metodi GET e POST all’interno dei form
Benché tecnicamente queste operazioni potrebbero essere realizzate con richieste POST, è buona pratica aderire a questo
standard

Una possibile soluzione è quella di inviare richieste POST ad Express ed utilizzare il package method-override per
“sovrascrivere” un metodo in base alla query string dell’URL di richiesta

const methodOverride = require(“method-override”);


[Link](methodOverride(“_method”, {
methods: [“POST”, “GET”]
}));

In questa maniera una POST all’URL /students/:studentID/update?_method=PUT sarà interpretata come una PUT

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Esercizi
Esercizio 8.3 (segue)

Realizzare le seguenti nuove funzionalità


• Visualizzazione delle informazioni di un singolo studente
• Visualizzazione delle informazioni di un singolo corso di laurea
• Modifica di un singolo studente
• Modifica di un singolo corso di laurea
• Eliminazione di un singolo studente
• Eliminazione di un singolo corso di laurea

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Realizzazione
di API
Nella prima parte di questa introduzione ad Express abbiamo realizzato delle risposte che fornissero agli utenti una vista, ad
es. mediante un approccio a template (server-side rendering)
Andiamo adesso a generalizzare, realizzando delle REST API che siano capaci non solo di fornire viste HTML in risposta alle
richieste, ma che possano essere capaci di far interagire i client (non solo i browser!) con le nostre risorse e rispondere con
qualunque altro formato di dati
Questa strategia è utile, ad esempio, per realizzare Single Page Application (client-side rendering) che richiedono
l’informazione, ricevono l’informazione in formato JSON e modificano di conseguenza la pagina dell’applicazione oppure è
utile per fornire a terze parti l'accesso alla nostra informazione
Inoltre, faremo in modo che ciascun utilizzatore acceda solo alle risorse per le quali è autorizzato ad accedere
Per prima cosa, per mettere ordine, ricordiamoci di organizzare la nostra app mediante namespace di route, spostando le
route di ciascun namespace in un router a sé stante

const router = [Link](); const router = require(‘./routes/index’);


[Link](‘/students’, userRouter); const app = express();
[Link](‘/courses’, courseRouter); [Link](router);
[Link](‘/‘, homeRouter);
[Link](‘/‘, errorRouter);
[Link] = router;
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Realizzazione
di API
Scrivere un controller che restituisca un JSON anziché dati generici è possibile grazie al metodo json() sull’oggetto di
risposta, che prende in input l’oggetto da serializzare

[Link]([Link]);

Possiamo anche immaginare un controller che supporti diverse tipologie di risposta in base alla richiesta effettuata, ad
esempio in base alla query string potrebbe decidere di fare rendering o meno

indexView: (req, res) => {


if ([Link] === ‘json’ {
[Link]([Link]);
} else {
[Link](‘courses’);
}
}

A questo punto il client potrà fare una richiesta AJAX appendendo ?format=json al path per ottenere il file JSON da
applicare client-side alla vista
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Realizzazione
di API
Un’altra idea è quella di realizzare un namespace apposito per l’endpoint di API ed utilizzare il relativo middleware
all’interno del router, in maniera tale da raggiungere la stessa API all’indirizzo /api/courses

const apiRoutes = require(“./apiRoutes”);


[Link]("/api", apiRoutes)

Il router per le API potrebbe essere implementato nella maniera seguente: Ricordiamo che i controller
vo n o ve ri fic a re c h e l’u te n te sia
d e
const router = require(‘express’).Router(), loggato, ma…
coursesController = require(‘../controllers/courses’);

[Link](‘/courses’, [Link], [Link]);


[Link]([Link]);
[Link] = router;

Chiaramente, dobbiamo supporre di aver creato due middleware appositi per l’invio di risposte JSON

respondJSON: (req, res) => {[Link]({status: 200, data: [Link]})}


Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione e autorizzazione sono due passaggi fondamentali per il mantenimento della sicurezza e dell'integrità di
un'applicazione web

Autenticazione

Processo di verifica dell'identità di un utente (es. validazione di credenziali, sessione o token)


1. Registrazione dell'utente: creare un form di registrazione, fare "hash with salt" della password, memorizzare username,
password hashata (e salt) nel database
2. Login: creare un form di login, validare le credenziali con quelle memorizzate nel database, se le credenziali sono corrette
creare una sessione o generare un token per mantenere l'utente loggato

Autorizzazione

Determina se un utente è abilitato a svolgere una certa operazione all'interno dell'applicazione


Per far ciò è necessario definire ruoli e permessi, ad esempio mediante Role-based Access Control: definire ruoli all'interno
dell'applicazione (es. admin, editor, utente), assegnare i permessi a ciascun ruolo, implementare middleware che controllino
ruolo e permessi prima di permettere l'accesso a certe operazioni

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Esempio base

Per iniziare, supponiamo di utilizzare un meccanismo estremamente semplice


Supponiamo di creare un token segreto che rappresenterà la keyword d’accesso alle nostre API e di fornirlo agli utenti fidati
Potremo quindi aggiungere un middleware che verifichi che il client che ha fatto richiesta all’endpoint abbia inserito il giusto
token all’interno della query string

verifyToken: (req, res, next) => {


if ([Link] === token) next();
else next(new Error(“Token non valido.”));
}

// Protected route
[Link]('/', verifyToken, (req, res) => {
[Link](200).json({ message: 'Protected route accessed' });
});

Chiaramente si tratta di una soluzione artigianale che diventa “pericolosa” se il token viene diffuso in modo incontrollato
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Gestione di registrazione e login

Un approccio per gestire la registrazione ed il login è quello di prevedere un campo per la password nello schema degli utenti
Al fine di salvare le password in maniera hash&salt all’interno del database, potremmo utilizzare bcrypt
Ad esempio un pre hook potrebbe gestire l’hashing della password prima del salvataggio nel database e un metodo di
istanza passwordComparison sullo schema degli utenti potrebbe effettuare la comparazione della password inserita
durante il login con la password hashata memorizzata nel database

const userSchema = [Link]({ [Link]("save", function(next) {


firstName: String, let user = this;
lastName: String, [Link]([Link], 10).then(hash => {
email: { [Link] = hash;
type: String, next();
unique: true });
});
},
password: String
[Link] =
}); function(inputPassword) {
let user = this;
return [Link](inputPassword, [Link]);
};

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione stateful

Il server crea una sessione per l'utente dopo il login e memorizza l'informazione di sessione lato server
Il server invierà un cookie contenente l'ID di sessione al browser dell'utente, che sarà poi memorizzato e rinviato dall'utente ad
ogni richiesta successiva
Nel cookie non ci saranno dati, ma solo un ID di sessione: i dati saranno memorizzati lato server, grazie ad un apposito store
In Express, possiamo usare la libreria express-session che, letto il cookie, imposta i dati di sessione in [Link]

[Link](session({ secret: 'segreto_per_firmare_il_cookie', cookie: { maxAge: 60000 }}))

[Link]('/', function(req, res, next) {


if ([Link]) {
[Link]++
} else {
[Link] = 1
}
[Link]({"message": "You visited this resource ${[Link]}
and your cookie will expire in ${[Link] / 1000} seconds"})
})

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione stateful
[Link]('/login', (req, res) => { function isAuthenticated (req, res, next) {
// verifica username e password, poi... if ([Link]) next()
if (authenticated) { else next('route')
[Link](() => { }
// memorizza le informazioni necessarie
// nella sessione, ad esempio lo username [Link](session({
[Link] = [Link] secret: 'segreto_per_firmare_il_cookie',
[Link](() => { cookie: { maxAge: 60000 }
[Link]('/') }))
})
}) [Link]('/', isAuthenticated, function (req, res) {
}) [Link]('hello, ' + [Link] + '!')
})
[Link]('/logout', (req, res, next) => {
[Link] = null [Link]('/', function (req, res) {
[Link](() => { [Link](loginPage)
[Link](() => { })
[Link]('/')
})
})
})
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione stateless

Il server non conserva alcuna informazione sulla sessione, pertanto ogni richiesta conterrà tutti i dati necessari per autenticare
l'utente
Se necessario, queste informazioni vengono memorizzate dal client

Si possono utilizzare schemi come la Basic Authentication di HTTP, oppure Access Token come JWT e Bearer token

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione stateless con Basic Autenthication

const basicAuth = require('express-basic-auth')

[Link]('/',
basicAuth({
users: { username: "password" }, // Elenco (o funzione) di utenti e password ammessi
challenge: true,
unauthorizedResponse:
"Unauthorized access. Please provide valid credentials.",
})
);

[Link]("/secure-data", (req, res) => {


[Link]("This is secure data that requires valid credentials.");
});

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione stateless con access token JWT

Il JSON Web Token (RFC 7519) è uno standard aperto e usato a livello industriale per trasmettere informazione in maniera
sicura e self-contained fra due parti

Un JWT è rappresentato da tre parti, poi codificate in Base64Url e separate fra loro mediante punti

{ { HMACSHA256( eyJhbGciOiJIUzI1NiIsInR5c
"alg": "HS256", "sub": "1234567890", base64UrlEncode(header) [Link]
"typ": "JWT" "name": "John Doe", + "." + jM0NTY3ODkwIiwibmFtZSI6Ik
} "admin": true base64UrlEncode(payload),
} "mio_segreto" pvaG4gRG9lIiwiYWRtaW4iOnR
) ydWV9.VFnB5SMAgpV0dVLNxkm
ueMxPguP9J7D8KFTBi7ls4mM

Header Payload Signature JWT risultante


Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione stateless con access token JWT

Supponiamo, quindi, di voler realizzare un meccanismo di login alle nostre API che produca un access token JWT
Per far ciò, in [Link], possiamo utilizzare la libreria jsonwebtoken

const jsonWebToken = require("jsonwebtoken");

login: (req, res, next) => {


const user = loginFunction([Link], [Link])
if (user) {
const signedToken = [Link](
{
data: user._id,
exp: new Date().setDate(new Date().getDate() + 1)
},
"secret_encoding_passphrase"
);
[Link]({ success: true });
// IN QUALCHE MODO DOBBIAMO ANCHE INVIARE IL TOKEN ALL'UTENTE
} else {
[Link]({ success: false, message: "Could not authenticate user." });
}
}
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Autenticazione stateless con access token JWT

Alle richieste successive, il client invierà nuovamente il token, che sarà utilizzato all’interno di un middleware per il controllo dei
dati di autenticazione
verifyJWT: (req, res, next) => {
let token = ...; // DOBBIAMO RECUPERARE IL TOKEN DALLA RICHIESTA DELL'UTENTE
if (token) {
const decoded = [Link](token, "secret_encoding_passphrase")
if (decoded) {
[Link]([Link]).then(user => {
if (user) {
[Link] = user._id
next(); // Protected route
} else { [Link]('/', verifyJWT, (req, res) => {
[Link]([Link]).json({ [Link](200).json({
error: true, message: 'Protected route accessed' });
message: "No User account found." });
});
}
})
} else { ... }
} else { ... }
}
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza Nota b e n e. U n
re
ac
re
ce
vo
ss
c
to
ato/
k e n

delle API
s e
non può es s c a d e da
fi n c hé n on
inva l i da to Puoi approfondire qu
i, qui e qui
solo
Autenticazione stateless con access token JWT

Come detto, il JWT conterrà le informazioni sull’utente e il client dovrà sempre rispedirlo al server che l'ha generato
Ma come può fare il client per memorizzare questo token e rispedirlo al server?

Memorizzazione del token nel localStorage del client e invio come Bearer token nell'header Authorization
Soggetto ad attacchi di tipo XSS (Cross-Site Scripting)
Invio da parte del server Memorizzazione sul client Invio di richieste successive da parte
[Link]({ jwtToken: token }); [Link]('jwtToken', <token>) del client con header
Authorization: Bearer <token>
Memorizzazione del token come cookie
Se non adeguatamente impostato, soggetto ad attacchi di tipo CSRF (Cross-Site Request Forgery)
Invio da parte del server Invio di richieste successive da parte del client con cookie in automatico (su HTTP)
[Link]("jwtToken", signedToken, { Per Fetch API tramite JS può essere necessario impostare
expires: ..., fetch('[Link]
path: '/', ...,
httpOnly: true, credentials: 'include'
secure: true, });
sameSite: 'strict'
})
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza La strategia che unisce agli access token anche i refresh token
permette di aumentare il livello di sicurezza
delle API L'access token, che permette l'accesso diretto alle risorse, avrà
Autenticazione con access token e refresh token scadenza breve o brevissima

L'access token potrà essere rigenerato, di volta in volta, grazie a un


refresh token

In questa maniera l'utente può rimanere autenticato a lungo


rinnovando gli access token in background, mediante una
esperienza fluida

Il refresh token è gestito lato server, quindi è revocabile, ovvero


potremo andare ad impedire la generazione di nuovi access token
Potremo decidere di revocare un refresh token se un utente si
disconnette o se si sospetta un attacco

In ogni caso, con access token corti e refresh token sicuri, un


eventuale attaccante dovrà necessariamente rubare due oggetti

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
La funzione di login dovrà anzitutto controllare la correttezza di

Sicurezza username e password, poi generare access token e refresh token

delle API
const accessToken = [Link]({
_id: user._id,
role: [Link]
}, ACCESS_TOKEN_SECRET, {expiresIn: '10s'})
Autenticazione con access token e refresh token
const refreshToken = // GENERA UNA STRINGA OPACA (O UN JWT)

Questi due oggetti verranno inviati all'applicazione client: il primo, ad


esempio, nel corpo della risposta, il secondo come cookie HTTPOnly

Il nostro server, per le API protette, dovrà verificare l'access token –


ricevuto ad esempio nell'header Authorization –, utilizzando ad
esempio il middleware verifyJWT costruito precedentemente

[Link](ROUTE_NON_PROTETTA, CONTROLLER)
[Link]([Link])
[Link](ROUTE_PROTETTA, CONTROLLER)
[Link](ROUTE_PROTETTA, CONTROLLER)
...

Cosa succederà quando, dopo 10 secondi, l'access token sarà


scaduto?

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Dovremo dare la possibilità all'utente di rigenerare un nuovo

Sicurezza access token mediante un controller che verifica il refresh token

delle API refreshToken: async (req, res) => {


try {
const cookies = [Link]
if (!cookies?.refreshToken) ... // REFRESH TOKEN
Autenticazione con access token e refresh token NON PRESENTE
const refreshToken = [Link]
const refreshObj = await [Link]({value:
refreshToken})
if (!refreshObj) ... // REFRESH TOKEN NON VALIDO
const user = await [Link]([Link])
const accessToken = [Link]({
_id: refObj._id,
role: [Link]
}, ACCESS_TOKEN_SECRET, {expiresIn: '10s'})
...
} catch (e) {
...
}
}

Questo significa che avremo bisogno di:


• Un modello per conservare i refresh token validi con un
riferimento all'utente
• Una route non protetta per accedere al controller
refreshToken

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
OAuth2.0 per l'autorizzazione

Una scelta avanzata per l'autorizzazione delle nostre API è quella di utilizzare il protocollo
OAuth2.0 (Open Authorization), che rappresenta uno standard per l'autorizzazione
delegata delle nostre API

OAuth è fondamentale per proteggere le nostre API in un contesto third-party, ovvero


quando un utente vuole autorizzare un'altra applicazione ad accedere ai suoi dati
memorizzati altrove ad esempio nella nostra applicazione
Il meccanismo OAuth prevede di effettuare il login all’interno di un OAuth Server, che
autorizza altre applicazioni ad accedere alle nostre API

Ad esempio (vedi immagine a destra), prima di OAuth, Facebook ci chiedeva di inserire


direttamente la password di Gmail per scaricare tutti i nostri contatti (quindi Facebook
rappresentava un utilizzatore third-party delle API di Gmail)
Con OAuth, invece, inseriamo la nostra password direttamente dentro il form di accesso di Google (ovvero veniamo reindirizzati
all’OAuth Server di Google) e poi veniamo rispediti indietro all’applicazione

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
OAuth2.0 per l'autorizzazione

Il cuore del protocollo OAuth è l’Access Token, ovvero una stringa “segreta” che l'applicazione client OAuth utilizza per fare
richieste al server essendo autorizzata ad agire al posto dell'utente

Oltre ad Authorization Code, esistono anche altri flow per esigenze


differenti
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
OpenID e OpenID Connect per l'autenticazione

Il meccanismo OAuth, da solo, è un meccanismo di autorizzazione (e non di


autenticazione)
Per l'autenticazione federata esiste il meccanismo OpenID che permette a
una terza parte di autenticare gli utenti utilizzando account che già
possiedono su un identity provider (es. [Link]) senza dover
condividere le proprie credenziali
OpenID, in pratica, fornisce un'asserzione di identità
Questo sistema è anche utile anche in scenari first-party: se costruiamo
diverse applicazioni che utilizzano le nostre stesse API, potremmo utilizzare
un OAuth Server per garantire l’accesso agli utenti a tutte le nostre app
Ad esempio, Google permette di effettuare l’accesso mediante il suo OAuth
server e poi utilizzare sia Gmail che YouTube

OpenID Connect è un layer di autenticazione costruito su OAuth, che


combina le caratteristiche di OpenID e di OAuth (lavorando con i JWT)

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

Passport è un middleware di autenticazione per [Link] che incapsula la sola funzionalità di autenticazione delle richieste,
delegando altri dettagli all'applicazione (separation of concerns)
Passport permette ad un'applicazione di fare uso di un "qualunque" meccanismo di autenticazione, ovvero una qualunque
strategia, ad esempio mediante login locale, sia mediante servizi di terze parti (es. Facebook, Google, …)

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

La gestione dell'autenticazione con Passport avviene semplicemente attraverso un middleware che, in caso di successo,
imposterà l'oggetto [Link] all'utente autenticato, stabilirà una sessione e chiamerà il middleware successivo

[Link]('/login/password',
[Link]('local', { failureRedirect: '/login', failureMessage: true }),
function(req, res) {
[Link]('/~' + [Link]);
});

Il meccanismo utilizzato per autenticare la richiesta è implementato da una particolare strategia (nell'esempio in alto, quella
locale che permette di verificare username e password)
Dal momento che Passport implementa numerosissime strategie, il meccanismo di autenticazione definirà come codificare e
come verificare delle credenziali, ad es. un password oppure un'informazione proveniente da un identity provider

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

Come detto, è necessario specificare come deve essere trattata la richiesta di autenticazione dell'utente
Ad esempio, la strategia locale legge le credenziali contenute nella richiesta (di default, le proprietà username e password
del corpo della POST) e chiama una funzione (specificata come parametro del costruttore di LocalStrategy) che è
responsabile di determinare l'identità dell'utente

const LocalStrategy = require('passport-local');

[Link](new LocalStrategy(
function(username, password, done) {
[Link]({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (![Link](password)) { return done(null, false); }
return done(null, user);
});
}
));
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

Passport può fare uso delle sessioni per mantenere lo stato di autenticazione dell'utente

const expressSession = require("express-session");


const passport = require(“passport");
const LocalStrategy = require(‘passport-local').Strategy;
const User = require("./models/user");

[Link](expressSession({secret: "secret_passcode"}));
La serializzazione permetterà di
[Link]([Link]());
[Link]([Link]()); memorizzare l'oggetto utente all'interno
della sessione
[Link](new LocalStrategy(...); Alle richieste successive, una volta
[Link](function(user, done) {
done(null, { id: [Link], username: [Link] }); autenticato l'utente, l'oggetto viene
}); recuperato dalla sessione e
[Link](function(user, done) { deserializzato, per essere quindi
done(null, user)
impostato come oggetto [Link]
});
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

Infine, Passport offre alcuni metodi e proprietà interessanti sull’oggetto di richiesta


In particolare, si tratta di informazioni relative allo stato dell’utente e all’utente stesso, nonché un metodo per effettuare il
logout dell’utente
Queste informazioni sono contenute nell’oggetto di richiesta in quanto vengono inviate dal client al server mediante cookie

[Link]();
// true o false

[Link];

[Link]();

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

L’uso del plugin di Mongoose passport-local-mongoose è conveniente per gestire in maniera semplice la connessione fra
passport-local e Mongoose

const mongoose = require('mongoose');


const passportLocalMongoose = require('passport-local-mongoose');

const userSchema = [Link]({ email: String, … });

[Link](passportLocalMongoose, {
usernameField: “email”
});

[Link] = [Link](“User”, userSchema);


In particolare, questo plugin si occuperà di definire automaticamente il campo per lo username (a meno che non
specificato esplicitamente come nell’esempio), il campo per la password hashata (hash) e il campo per il salt value
(salt)
Il campo per la password nello schema utente andrà pertanto rimosso
Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

A questo punto siamo pronti per gestire la registrazione e il login degli utenti all’interno dei controller della nostra
applicazione

create: (req, res, next) => {


const newUser = new User({email: [Link]});
[Link](newUser, [Link], (error, user) => {
if (user) next();
else …
});
},

authenticate: [Link]("local", {
failureRedirect: “/users/login”,
failureFlash: “Login non riuscito”,
successRedirect: “/“,
successFlash: “Login riuscito”
})

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026
Sicurezza
delle API
Passport

Anche la gestione dell'autenticazione e della sessione saranno semplificate

const expressSession = require("express-session");


const passport = require(“passport");
const LocalStrategy = require(‘passport-local').Strategy;
const User = require("./models/user");

[Link](expressSession({secret: "secret_passcode"}));
[Link]([Link]());
[Link]([Link]());

[Link](new LocalStrategy([Link]());
[Link]([Link]());
[Link]([Link]());

Antonio Ferrara | Fondamenti del Web (Ingegneria del Software e Fondamenti Web) | A.A. 2025/2026

Potrebbero piacerti anche