Visualizzazione post con etichetta express session management. Mostra tutti i post
Visualizzazione post con etichetta express session management. Mostra tutti i post

lunedì, gennaio 12, 2015

Biblioteca: altro modulo per gestire la logica di business

Oggi implementeremo le funzionalità core di login e registrazione.

Non introdurremo ancora l'interazione con un server di dati per non appesantire l'esposizione, utilizzeremo una struttura dati in memoria per memorizzare le utenze, ma struttureremo il codice in maniera tale da non dover modificare la parte di gestione delle varie richieste presenti nel modulo delle rotte degli utenti: come? Ma utilizzando un altro bel raviolone :).

Prima di mettere il naso nel codice, navigate l'applicazione aggiornata su heroku per capire dove vogliamo andare a parare: https://whispering-peak-1284.herokuapp.com.

Provate a fare il login con username ivan e password saracino, che ha un profilo admin, notate come il menu presenti funzionalità (da implementare) di amministrazione.

Fate il logout per pulire la sessione e entrate nella pagina di registrazione, inserendo una vostra utenza; verrete rediretti alla pagina di login con un opportuno messaggio per effettuare l'autenticazione con le credenziali appena create: rifate il login e notate come cambia il menu e i messaggi di benvenuto presenti nell'header.

Ad ogni riavvio della applicazione, ovviamente tutte le utenze create non ci saranno più, rimarrà valido solo l'utente admin ivan saracino.

Per capire come abbiamo strutturato il codice, date un'occhiata al seguente diagramma di oggetti


Vi ricordo che moduli nel contesto javascript non sono altro che oggetti che espongono particolari proprietà che possono essere anche funzioni.

Il diagramma in alto non dice altro che il modulo app utilizza il modulo delle rotte utenti che a sua volta utilizza il modulo serviziUtenze: grazie a questa separazione di responsabilità tra moduli diversi sarà facile tenere sotto controllo la complessità del nostro progetto.

Il modulo app ha la responsabilità di configurare l'intera applicazione, tramite, ad esempio, l'utilizzo di moduli particolari di express per la gestione delle sessioni o il parsing del corpo delle richieste http: per poter effettuare il post dei form, dovremo includere nel codice del modulo app le seguenti istruzioni
 var bodyParser = require('body-parser');  
 ...  
 app.use(bodyParser.urlencoded({ extended: true }));   
In questo modo sarà possibile estrarre facilmente i parametri mandati in post al server al submit del form.

Il modulo utenti ha la responsabilità di definire le funzioni da eseguire per gestire le varie funzionalità legate agli utenti quali il login e la registrazione.
 var express = require('express');  
 var router = express.Router();  
 var serviziBiblioteca = require('../servizi/serviziUtenze');  
   
 router.route('/login')  
      .get(function(request,response) {  
           response.render('login');  
      })  
      .post(function(request,response){  
           var utente = serviziBiblioteca.login(  
                request.body.username,  
                request.body.password  
           );  
           if (utente) {  
                request.session.utente = utente;       
                response.render('home');  
           }  
           else {  
                response.render(  
                     'login',   
                     { loginFallito : 'Utente non abilitato'}  
                );  
           }  
             
      });  
   
 router.route('/registrazione')  
      .get(function(request,response) {  
           response.render('registrazione');  
      })  
      .post(function(request,response) {  
           var utente = {};  
           utente.username = request.body.username;  
           utente.password = request.body.password;  
           utente.email = request.body.email;  
           utente.profilo = 'semplice';  
           serviziBiblioteca.registra(utente);  
           response.render('login', {  
                registrazioneOk: 'Ora puoi effettuare il login!'});  
      });  
   
 router.route('/logout')  
      .get(function(request,response) {  
           request.session.destroy(function() {  
                response.redirect('/');  
           });  
      });  
   
 module.exports = router;  
Notate dal codice come sia possibile gestire una richiesta sia in get che in post.

Prendiamo ad esempio la richiesta /utente/login e osserviamo come nel caso di una richiesta get venga semplicemente renderizzata la pagina login.jade, mentre nel caso di una richiesta di tipo post venga utilizzato il modulo serviziUtenze per effettuare la vera e propria autenticazione.

Per estrarre i parametri dalla richiesta post, basta usare un codice del tipo request.body.nomeparametro.

Nel modulo serviziUtenze risiede la logica di business vera e propria, in questo caso abbiamo creato un modulo che espone due metodi login e registra che utilizzano un array di oggetti in memoria come base dati delle utenze inizializzato con l'utente admin ivan saracino.
 module.exports = (function(){  
      var utentiRegistrati = [  
           {  
                username : 'ivan',  
                password: 'saracino',  
                email: 'ivan.saracino@gmail.com',  
                profilo: 'admin'   
           }  
      ];  
      var login = function(username,password) {  
           var utentiLoggati = utentiRegistrati.filter(function(utente) {  
                return utente.username === username &&   
                    utente.password === password;  
           });  
           return utentiLoggati[0];  
      };  
      var registra = function(utente) {  
           utentiRegistrati.push(utente);  
           console.log(utentiRegistrati);  
      };  
      return {  
           login : login,  
           registra : registra  
      };  
 })();  
Per organizzare meglio i file, abbiamo previsto la presenza di una cartella servizi in cui memorizzare i file dei moduli della logica di business: non è difficile prevedere la presenza di un ulteriore modulo per la gestione dei libri quando andremo ad implementare tali funzionalità.

Per la generazione del menu dinamico, osserviamo il file menu.jade incluso nel layout
 if (!session.utente)  
      ul(class='nav nav-pils')  
           li(role='presentation')  
                a(href='/utente/login') Login  
           li(role='presentation')  
                a(href='/utente/registrazione') Registrati!  
 else  
      ul(class='nav nav-pils')  
           li(role='presentation')  
                a(href='/utente/logout') Logout  
           if (session.utente.profilo === 'semplice')  
                li(role='presentation')  
                     a(href='#') link semplice 1  
                li(role='presentation')  
                     a(href='#') link semplice 2  
           else  
                li(role='presentation')  
                     a(href='#') link admin 1  
                li(role="presentation")  
                     a(href='#') link admin 2  
Se esiste un utente in sessione, ne verrà controllato il profilo per generare opportunamente i link corretti.

Non abbiamo implementato una validazione dei parametri passati al server, nel prossimo post vedremo come affrontare questo argomento tramite l'utilizzo di un middleware opportuno.

Come al solito, ho aggiornato su github il progetto installato su heroku, in modo che possiate avere sempre tutto il codice a disposizione, in questo post è spiegato come utilizzare github.

Dopo aver fatto il pull del progetto, ricordatevi di eseguire npm install nella cartella di progetto per installare la dipendenza aggiuntiva per il modulo body-parser : se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan

sabato, dicembre 20, 2014

Gestione della sessione con express

Ogni applicazione web che si rispetti, deve saper gestire i dati di sessione.

Il protocollo http è stateless per natura: ogni richiesta http è indipendente dalle altre, per sviluppare applicazioni web dobbiamo essere in grado di capire quando una determinata richiesta appartiene alla sessione di comunicazione valida di un particolare client.

Il classico esempio per capire il concetto di dato di sessione è il carrello della spesa di un sito e-commerce: con una richiesta http inseriamo un prodotto nel carrello, con una seconda, indipendente richiesta, ne inseriamo un altro e così via: dobbiamo garantire  per ogni utente che sta acquistando, un proprio carrello della spesa!

Per gestire una sessione lato server, di solito si usa un meccanismo del genere:
  1. Alla prima richiesta di un client si genera un id (un numero intero) univoco per rappresentare la sessione
  2. Tramite il meccanismo dei cookies o url rewriting, si obblica il client a mandare con le successive richieste l'id della sessione, in modo da permettere al server di capire a quale sessione appartiene una determinata richiesta.
  3. Quando, per una determinata sessione, non arrivano più richieste entro un determinato intervallo di tempo (ad esempio 20 minuti), la sessione è considerata scaduta e tutti i dati relativi vengono cancellati

Nella figura, alla prima richiesta il client manda un parametro al server, che crea un id di sessione e memorizza il valore del parametro come dato di sessione: nella prima risposta il server dice al client di memorizzare l'ID 12345 in un cookie.

Il client, con seconda richiesta, racchiude l'informazione dell'ID in modo che il server possa reperire i dati della sessione corrispondente a quell'identificatore.

Normalmente i dati di sessione sono salvati in memoria: se si riavvia il server i dati delle sessioni eventualmente attive vengono persi.

Vediamo come memorizzare e ottenere dati dalla sessione utilizzando express.

Dobbiamo utilizzare solo un paio di middleware e saremo in grado di accedere ai dati della sessione, questi middleware non sono inclusi nella distribuzione standard di express, dovremo installarli con npm con i seguenti comandi dalla cartella root del progetto

  • npm install cookie-parser --save
  • npm install express-session --save
L'opzione --save serve ad aggiungere le nuove dipendenze nel file package.json.

Nel file app.js, aggiungiamo le seguenti istruzioni per includere ed utilizzare i middleware necessari
 var express = require('express');  
 var cookieParser = require('cookie-parser');  
 var expressSession = require('express-session');  
   
 var app = express();   
   
 ...  
   
 app.use(cookieParser());  
   
 app.use(expressSession({  
      secret: 'stringaqualunque',  
      resave: false,  
      saveUninitialized: true    
 }));   
   
 app.use(function(req,res,next) {  
      res.locals.session = req.session;  
      next();   
 });  
L'ultimo use() serve a rendere disponibile al motore jade l'oggetto session che sarà sempre disponibile in ogni oggetto request.

Ho modificato l'applicazione su heroku https://whispering-peak-1284.herokuapp.com/ , nel file layout.jade ho inserito un codice condizionale di questo tipo:
 div.page-header  
                if (session.utente)  
                     | Benvenuto #{session.utente}  
                else  
                     | Non hai ancora inserito un   
                     | nome per il saluto! Appena   
                     | lo farai verrà messo in sessione  
Nella divisione con classe page-header, testo la presenza di un oggetto utente in sessione:  lo possiamo fare solo grazie alla definizione dell'ultimo middleware visto in precedenza.

Quando si naviga il sito la prima volta e prima di utilizzare il form, comparirà il messaggio del ramo else, dopo aver usato il form, verrà creata una proprietà utente nell'oggetto sessione valorizzata con il parametro che arriva al submit del form: da questo momento in avanti, anche facendo altre richieste, comparirà sempre il messaggio del ramo if, che va a prelevare il dato dalla sessione.

Il codice per memorizzare il nome in sessione al submit del form è semplicemente il seguente:
 app.get('/benvenuto', function(req,res){  
      req.session.utente = req.query.nome;  
      res.render('benvenuto', {nomeCognome : req.query.nome});  
 });   
Notate bene: senza cookie-parser e express-session non avremmo un oggetto session come proprietà della request.

Ho aggiornato su github il progetto installato su heroku, in modo che possiate avere tutto il codice a disposizione, in questo post è spiegato come utilizzare github.

Dopo aver fatto il pull del progetto, ricordatevi di eseguire npm install nella cartella di progetto per installare le dipendenze aggiuntive: se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan