Mysql
 sql >> Database >  >> RDS >> Mysql

Come gestire le sessioni in Node.js utilizzando Passport, Redis e MySQL

HTTP e HTTPS sono protocolli Internet che consentono di inviare dati su Internet inviando una richiesta tramite un browser web. Poiché sono stateless, ogni richiesta inviata al browser viene trattata in modo indipendente. Ciò significa che il browser non può ricordare l'origine di una richiesta, anche se lo stesso utente la effettua. Le sessioni HTTP risolvono questo problema.

Questo articolo esaminerà la gestione delle sessioni e come strumenti come Passport, Redis e MySQL possono aiutarci a gestire le sessioni di Node.js. Entriamo.

Come funzionano le sessioni HTTP?

Le sessioni HTTP consentono ai server Web di mantenere l'identità dell'utente e di archiviare dati specifici dell'utente in più interazioni di richiesta/risposta tra un'app client e un'app Web. Quando un client accede all'applicazione, il server genera un SessionID. La sessione viene salvata in memoria utilizzando un meccanismo di archiviazione persistente non replicato a server singolo. Esempi di tali meccanismi includono la persistenza JDBC, la persistenza del file system, la persistenza della sessione basata su cookie e la replica in memoria. Quando l'utente invia una richiesta successiva, il sessionID viene passato nell'intestazione della richiesta e il browser controlla se l'ID corrisponde a uno qualsiasi nella memoria di archiviazione e concede all'utente l'accesso fino alla scadenza della sessione.

Le sessioni HTTP memorizzano i seguenti dati in memoria:

  • Specifiche sulla sessione (identificatore di sessione, ora di creazione, ora dell'ultimo accesso, ecc.)
  • Informazioni contestuali sull'utente (ad esempio, stato di accesso del client)

Che cos'è Redis?

Redis (Remote Dictionary Server) è un archivio dati chiave-valore in memoria veloce, open source, utilizzato come database, cache, broker di messaggi e coda.

Redis ha tempi di risposta inferiori al millisecondo, consentendo milioni di richieste al secondo per applicazioni in tempo reale in settori come giochi, tecnologia pubblicitaria, finanza, assistenza sanitaria e IoT. Di conseguenza, Redis è ora uno dei motori open source più popolari, essendo stato nominato il database "Più amato" da Stack Overflow per cinque anni consecutivi. Grazie alle sue prestazioni veloci, Redis è una scelta popolare per memorizzazione nella cache, gestione delle sessioni, giochi, classifiche, analisi in tempo reale, geospaziale, ride-hailing, chat/messaggistica, streaming multimediale e pub/app secondarie.

Cosa stiamo costruendo?

Per dimostrare la gestione delle sessioni in Node.js, creeremo una semplice applicazione di registrazione e accesso. Gli utenti si iscriveranno e accederanno a questa applicazione fornendo il proprio indirizzo e-mail e password. Una sessione viene creata e salvata nell'archivio Redis per richieste future quando un utente accede. Quando un utente si disconnette, elimineremo la sua sessione. Basta parlare; iniziamo!

Prerequisiti

Questo tutorial è una dimostrazione pratica. Assicurati di aver installato quanto segue prima di iniziare:

  • Node.js
  • CLI Redis
  • Database MySQL
  • Tipo Arco

Il codice per questo tutorial è disponibile sul mio repository Github. Sentiti di clonare e seguire.

Configurazione del progetto

Iniziamo creando una cartella di progetto per l'applicazione con il comando seguente:

mkdir Session_management && cd Session_management

Quindi, inizializza un'applicazione Node.js per creare un file package.json con il comando seguente:

npm init -y

Il -y flag nel comando precedente dice a npm di utilizzare la configurazione predefinita. Ora crea la seguente struttura di cartelle nella directory principale del tuo progetto.

Con il nostro package.json creato, installiamo il pacchetto richiesto per questo progetto nella prossima sezione.

Installazione delle dipendenze

Installeremo le seguenti dipendenze per la nostra applicazione:

  • Bcryptjs - Questo modulo verrà utilizzato per eseguire l'hashing della password dell'utente.
  • Connect-redis - Questo modulo fornirà l'archiviazione della sessione Redis per Express.
  • Sessione rapida - Questo modulo verrà utilizzato per creare sessioni.
  • Ejs - Questo modulo è il nostro motore di modelli
  • Passaporto - Questo modulo verrà utilizzato per l'autenticazione dell'utente
  • Passaporto locale - Questo modulo verrà utilizzato per l'autenticazione locale di nome utente e password
  • Sequenza - Questo modulo è il nostro ORM MySQL per connettere la nostra applicazione al database MySQL.
  • Dotenv - Questo modulo verrà utilizzato per caricare le nostre variabili di ambiente.

Utilizzare il comando seguente per installare tutte le dipendenze richieste.

npm install bcryptjs connect-redis redis express-session ejs passport passport-local sequelize dotenv

Attendi il completamento dell'installazione. Una volta completata l'installazione, procedi con la configurazione del database MySQL nella sezione successiva.

Configurazione del database MySQL

Creeremo un database MySQL per la nostra applicazione. Ma prima, esegui il comando seguente per creare un account utente MySQL.

CREATE USER 'newuser'@'localhost' IDENTIFIED BY '1234';

Ora crea un database session_db e concedi al nuovo utente l'accesso al database con il comando seguente:

#Create database
CREATE DATABASE session_db; 

 #grant access
GRANT ALL PRIVILEGES ON session_db TO 'newuser'@'localhost';

ALTER USER 'newuser'@'localhost' IDENTIFIED WITH mysql_native_password BY '1234';

Ora ricarica tutti i privilegi con il comando seguente:

FLUSH PRIVILEGES;

Con la nostra configurazione del database MySQL, creiamo i nostri users modello di database nella sezione successiva.

Crea un server rapido

Con la nostra configurazione del database MySQL, creiamo un server express per la nostra applicazione. Apri il file src/server.js e aggiungi lo snippet di codice di seguito:

const express = require("express");

const app = express();
const PORT = 4300;


//app middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

//Redis configurations

//Configure session middleware


//Router middleware


app.listen(PORT, () => {
 console.log(`Server started at port ${PORT}`);
});

Nello snippet di codice sopra, creiamo un server express, che ascolterà le richieste sulla porta 4300. Quindi, analizziamo le richieste in arrivo con i payload JSON usando express.json() middleware e analizzare le richieste in arrivo con urlencoded utilizzando Express.urlencoded() middleware.

Crea il modello di database

A questo punto, il nostro server Express è impostato. Ora creeremo un Users modello per rappresentare i dati dell'utente vedremo il database usando Sequelize . Apri src/models/index.js file e aggiungi lo snippet di codice di seguito.

const { Sequelize, DataTypes } = require("sequelize");
const sequelize = new Sequelize({
 host: "localhost",
 database: "session_db",
 username: "newuser",
 password: "1234",
 dialect: "mysql",
});

exports.User = sequelize.define("users", {
 // Model attributes are defined here
 id: {
   type: DataTypes.INTEGER,
   autoIncrement: true,
   primaryKey: true,
 },
 email: {
   type: DataTypes.STRING,
 },
 password: {
   type: DataTypes.STRING,
 },
});

Nello snippet di codice sopra, importiamo Sequelize e DateTypes da sequelize per connetterci al nostro database MySQL e assegnare un tipo di dati alle nostre proprietà del modello. Quindi, ci colleghiamo a MySQL creando un sequelize istanza da Sequelize classe e passando le credenziali del nostro database. Ad esempio, con sequelize ad esempio, abbiamo definito il nostro modello e le sue proprietà. Vogliamo solo i campi ID, e-mail e password di questo tutorial. Ma sequelize crea due campi aggiuntivi, il createdAt e updatedAt campi.

Imposta passaporto e Redis

Per gestire e memorizzare le credenziali dei nostri utenti, utilizzeremo e configureremo Redis . Per farlo, apri src/index.js file e importa le seguenti dipendenze di seguito:

const session = require("express-session");
const connectRedis = require("connect-redis");
const dotenv = require("dotenv").config()
const { createClient } = require("redis");
const passport = require("passport");

Quindi, individua l'area commentata //Redis configurations e aggiungi lo snippet di codice qui sotto:

const redisClient = createClient({ legacyMode: true });
redisClient.connect().catch(console.error);
const RedisStore = connectRedis(session);

Nello snippet di codice sopra, abbiamo stabilito una connessione al nostro database, che gestirà i dati del nome utente del nostro utente.

Quindi, individua l'area commentata //Commented session middleware e aggiungi lo snippet di codice qui sotto:

//Configure session middleware
const SESSION_SECRET = process.env.SESSION_SECRET;

app.use(
 session({
   store: new RedisStore({ client: redisClient }),
   secret: SESSION_SECRET,
   resave: false,
   saveUninitialized: false,
   cookie: {
     secure: false,  // if true only transmit cookie over https
     httpOnly: false, // if true prevent client side JS from reading the cookie
     maxAge: 1000 * 60 * 10, // session max age in milliseconds
   },
 })
);
app.use(passport.initialize());
app.use(passport.session());

Nello snippet di codice sopra, abbiamo creato un SESSION_SECRET variabile in un .env per mantenere la nostra sessione segreta, quindi ha creato un middleware di sessione e ha utilizzato Redis come nostro negozio. Affinché la sessione funzioni, aggiungiamo altri due middleware, il passport.initialize() e passport.session() .

Crea controller di applicazioni

Con il nostro Redis e la configurazione della sessione Express, creeremo un percorso per gestire le informazioni degli utenti. Per farlo, apri src/controllers/index.js file e aggiungi lo snippet di codice di seguito:

const { User } = require("../models");
const bcrypt = require("bcrypt");

exports.Signup = async (req, res) => {
 try {
   const { email, password } = req.body;

   //generate hash salt for password
   const salt = await bcrypt.genSalt(12);

   //generate the hashed version of users password
   const hashed_password = await bcrypt.hash(password, salt);

   const user = await User.create({ email, password: hashed_password });
   if (user) {
     res.status(201).json({ message: "new user created!" });
   }
 } catch (e) {
   console.log(e);
 }
};

Nello snippet di codice sopra, importiamo bcrypt e il nostro User modello, destrutturiamo l'email dell'utente e password dal req.body oggetto. Quindi abbiamo eseguito l'hashing della password utilizzando bcrypt e creato un nuovo utente utilizzando sequelize create metodo.

Quindi, crea una home page , registration page , login page con lo snippet di codice qui sotto:

exports.HomePage = async (req, res) => {
 if (!req.user) {
   return res.redirect("/");
 }
 res.render("home", {
   sessionID: req.sessionID,
   sessionExpireTime: new Date(req.session.cookie.expires) - new Date(),
   isAuthenticated: req.isAuthenticated(),
   user: req.user,
 });
};

exports.LoginPage = async (req, res) => {
 res.render("auth/login");
};

exports.registerPage = async (req, res) => {
 res.render("auth/register");
};

Nella HomePage , renderemo alcuni dei dettagli dell'utente autenticato insieme alla home visualizza.

Infine, crea il logout route, per eliminare i dati del nome utente dell'utente con lo snippet di codice di seguito:

exports.Logout = (req, res) => {
 req.session.destroy((err) => {
   if (err) {
     return console.log(err);
   }
   res.redirect("/");
 });
};

Crea la strategia Passport

A questo punto, gli utenti possono registrarsi, accedere e disconnettersi dalla nostra applicazione. Ora creiamo la strategia del passaporto per autenticare gli utenti e creare una sessione. Per farlo, apri src/utils/passport.js file e aggiungi lo snippet di codice di seguito:

const LocalStrategy = require("passport-local/lib").Strategy;
const passport = require("passport");
const { User } = require("../models");
const bcrypt = require("bcrypt");

module.exports.passportConfig = () => {
 passport.use(
   new LocalStrategy(
     { usernameField: "email", passwordField: "password" },
     async (email, password, done) => {
       const user = await User.findOne({ where: { email: email } });
       if (!user) {
         return done(null, false, { message: "Invalid credentials.\n" });
       }
       if (!bcrypt.compareSync(password, user.password)) {
         return done(null, false, { message: "Invalid credentials.\n" });
       }
       return done(null, user);

     }
   )
 );

 passport.serializeUser((user, done) => {
   done(null, user.id);
 });

 passport.deserializeUser(async (id, done) => {
   const user = await User.findByPk(id);
   if (!user) {
     done(error, false);
   }
   done(null, user);
 });
};

Nello snippet di codice sopra importiamo passport , bcrypt e il nostro modello utente, e creiamo un middleware del passaporto per utilizzare la local-strategy . Quindi rinominiamo il nome del file predefinito con i nomi dei campi ( email , password ) che stiamo utilizzando per autenticare gli utenti. Ora controlliamo se i dettagli dell'utente esistono nel database prima di poter creare una sessione per loro.

Il Passport.serialize e passport.deserialize i comandi vengono utilizzati per mantenere l'ID utente come cookie nel browser dell'utente e per recuperare l'ID dal cookie quando necessario, che viene quindi utilizzato per recuperare le informazioni dell'utente in una richiamata.

Il done() la funzione è un passport.js interno funzione che accetta l'ID utente come secondo parametro.

Crea i percorsi dell'applicazione

Con la nostra strategia per i passaporti creata, procediamo con la creazione di percorsi per i nostri controllori. Per farlo, apri src/routes/index.js file e aggiungi il seguente frammento di codice di seguito:

const express = require("express");
const {
 Signup,
 HomePage,
 LoginPage,
 registerPage,
 Logout,
} = require("../controllers");
const passport = require("passport");

const router = express.Router();

router.route("/").get(LoginPage);
router.route("/register").get(registerPage);
router.route("/home").get(HomePage);
router.route("/api/v1/signin").post(
 passport.authenticate("local", {
   failureRedirect: "/",
   successRedirect: "/home",
 }),
 function (req, res) {}
);
router.route("/api/v1/signup").post(Signup);
router.route("/logout").get(Logout);

module.exports = router;

Nello snippet di codice sopra, importiamo le nostre funzioni del controller e creiamo un percorso per loro. Per il signin route , abbiamo usato il passport.authenticate metodo per autenticare gli utenti utilizzando il local strategia impostata nella sezione precedente.

Ora torniamo al nostro server.js file, creeremo un middleware per i nostri percorsi. Prima di ciò, dobbiamo importare il nostro router e il passportConfig funzione.

const router = require("./routes");
const { passportConfig } = require("./utils/passport");

Quindi chiameremo passportConfig funzione subito sotto il codice nelle aree commentate //Configure session middleware .

passportConfig();

Quindi, creeremo il nostro middleware di percorso subito dopo l'area commentata//Router middleware .

app.use(router);

Crea le nostre visualizzazioni dell'applicazione

Con i nostri percorsi creati, creeremo viste renderizzate nella nostra HomePage , LoginPage e RegisterPage controllori. Prima di ciò, configureremo il nostro motore di visualizzazione ejs nel file server.js con uno snippet di codice in basso, proprio sotto l'area commentata //app middleware .

app.set("view engine", "ejs");

Quindi, inizieremo con la home page, apriamo views/home.ejs file e aggiungi il seguente markup.

<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Document</title>
   <link
     href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
     rel="stylesheet"
     integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
     crossorigin="anonymous"
   />
 </head>

 <body>
   <section>
     <!-- As a heading -->
     <nav class="navbar navbar-light bg-light">
       <div class="container-fluid">
         <a class="navbar-brand">Navbar</a>
         <% if(isAuthenticated){ %>
         <a href="/logout" class="btn btn-danger btn-md">Logout</a>
         <% } %>
       </div>
     </nav>
     <div class="">
       <p class="center">
         Welcome: <b><%= user.email %></b> your sessionID is <b><%= sessionID %></b>
       </p>
       <p>Your session expires in <b><%= sessionExpireTime %></b> seconds</p>
     </div>
   </section>
 </body>
</html>

Qui nella nostra home page, abbiamo utilizzato bootstrap per aggiungere uno stile ai nostri markup. Quindi controlliamo se l'utente è autenticato per mostrare il pulsante di logout. Inoltre visualizziamo l'Email dell'utente , sessionID e ExpirationTime dal back-end.

Quindi, apri src/views/auth/resgister e aggiungi il seguente markup di seguito per la pagina di registrazione.

<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Document</title>
   <link
     href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
     rel="stylesheet"
     integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
     crossorigin="anonymous"
   />
 </head>
 <body>
   <section class="vh-100" style="background-color: #9a616d">
     <div class="container py-5 h-100">
       <div class="row d-flex justify-content-center align-items-center h-100">
         <div class="col col-xl-10">
           <div class="card" style="border-radius: 1rem">
             <div class="row g-0">
               <div class="col-md-6 col-lg-5 d-none d-md-block">
                 <img
                   src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-login-form/img1.webp"
                   alt="login form"
                   class="img-fluid"
                   style="border-radius: 1rem 0 0 1rem"
                 />
               </div>
               <div class="col-md-6 col-lg-7 d-flex align-items-center">
                 <div class="card-body p-4 p-lg-5 text-black">
                   <form action="api/v1/signup" method="post">
                     <h5
                       class="fw-normal mb-3 pb-3"
                       style="letter-spacing: 1px"
                     >
                       Signup into your account
                     </h5>

                     <div class="form-outline mb-4">
                       <input
                         name="email"
                         type="email"
                         id="form2Example17"
                         class="form-control form-control-lg"
                       />
                       <label class="form-label" for="form2Example17"
                         >Email address</label
                       >
                     </div>

                     <div class="form-outline mb-4">
                       <input
                         name="password"
                         type="password"
                         id="form2Example27"
                         class="form-control form-control-lg"
                       />
                       <label class="form-label" for="form2Example27"
                         >Password</label
                       >
                     </div>

                     <div class="pt-1 mb-4">
                       <button
                         class="btn btn-dark btn-lg btn-block"
                         type="submit"
                       >
                         Register
                       </button>
                     </div>

                     <a class="small text-muted" href="#!">Forgot password?</a>
                     <p class="mb-5 pb-lg-2" style="color: #393f81">
                       Don't have an account?
                       <a href="/" style="color: #393f81">Login here</a>
                     </p>
                     <a href="#!" class="small text-muted">Terms of use.</a>
                     <a href="#!" class="small text-muted">Privacy policy</a>
                   </form>
                 </div>
               </div>
             </div>
           </div>
         </div>
       </div>
     </div>
   </section>
 </body>
</html>

Nella pagina di registrazione, abbiamo creato un modulo html per accettare i dettagli degli utenti. Nel modulo, aggiungiamo anche l'attributo attivo e specifichiamo l'endpoint di registrazione. Ciò significa che quando un utente fa clic sul pulsante di invio, verrà inviata una richiesta al /api/v1/signup punto finale.

Infine, apri src/views/auth/signin.js file e aggiungi il seguente snippet di markup di seguito:

<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Document</title>
   <link
     href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
     rel="stylesheet"
     integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
     crossorigin="anonymous"
   />
 </head>
 <body>
   <section class="vh-100" style="background-color: #9a616d">
     <div class="container py-5 h-100">
       <div class="row d-flex justify-content-center align-items-center h-100">
         <div class="col col-xl-10">
           <div class="card" style="border-radius: 1rem">
             <div class="row g-0">
               <div class="col-md-6 col-lg-5 d-none d-md-block">
                 <img
                   src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-login-form/img1.webp"
                   alt="login form"
                   class="img-fluid"
                   style="border-radius: 1rem 0 0 1rem"
                 />
               </div>
               <div class="col-md-6 col-lg-7 d-flex align-items-center">
                 <div class="card-body p-4 p-lg-5 text-black">
                   <form action="api/v1/signin" method="post">
                     <h5
                       class="fw-normal mb-3 pb-3"
                       style="letter-spacing: 1px"
                     >
                       Sign into your account
                     </h5>

                     <div class="form-outline mb-4">
                       <input
                         name="email"
                         type="email"
                         id="form2Example17"
                         class="form-control form-control-lg"
                       />
                       <label class="form-label" for="form2Example17"
                         >Email address</label
                       >
                     </div>

                     <div class="form-outline mb-4">
                       <input
                         name="password"
                         type="password"
                         id="form2Example27"
                         class="form-control form-control-lg"
                       />
                       <label class="form-label" for="form2Example27"
                         >Password</label
                       >
                     </div>

                     <div class="pt-1 mb-4">
                       <button
                         class="btn btn-dark btn-lg btn-block"
                         type="submit"
                       >
                         Login
                       </button>
                     </div>

                     <a class="small text-muted" href="#!">Forgot password?</a>
                     <p class="mb-5 pb-lg-2" style="color: #393f81">
                       Don't have an account?
                       <a href="/register" style="color: #393f81"
                         >Register here</a
                       >
                     </p>
                     <a href="#!" class="small text-muted">Terms of use.</a>
                     <a href="#!" class="small text-muted">Privacy policy</a>
                   </form>
                 </div>
               </div>
             </div>
           </div>
         </div>
       </div>
     </div>
   </section>
 </body>
</html>

Nel markup sopra, abbiamo aggiunto un modulo html che verrà utilizzato per accedere a un utente inviando una richiesta a /api/v1/signin punto finale.

Visualizza i dati degli utenti con Arctype

Ora abbiamo creato con successo un'applicazione di gestione della sessione Node.js. Diamo un'occhiata ai dati degli utenti con Arctype. Per iniziare, avvia Arctype, fai clic sulla scheda MySQL e inserisci le seguenti credenziali MySQL, come mostrato nella schermata seguente:

Quindi, fai clic su users tabella per mostrare gli utenti registrati come mostrato nello screenshot seguente:

Conclusione

Creando un'applicazione di accesso demo, abbiamo imparato come implementare la gestione delle sessioni in Node.js utilizzando Passport e Redis. Abbiamo iniziato con l'introduzione delle sessioni HTTP e del loro funzionamento, poi abbiamo esaminato cos'è Redis e creato un progetto per mettere in pratica tutto questo. Ora che hai le conoscenze che cerchi, come autenticheresti i progetti degli utenti?