Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment gérer les sessions dans Node.js à l'aide de Passport, Redis et MySQL

HTTP et HTTPS sont des protocoles Internet qui permettent d'envoyer des données sur Internet en envoyant une requête via un navigateur Web. Parce qu'ils sont sans état, chaque requête envoyée au navigateur est traitée indépendamment. Cela signifie que le navigateur ne peut pas se souvenir de la source d'une requête, même si c'est le même utilisateur qui la fait. Les sessions HTTP résolvent ce problème.

Cet article examinera la gestion des sessions et comment des outils tels que Passport, Redis et MySQL peuvent nous aider à gérer les sessions Node.js. Plongeons-y.

Comment fonctionnent les sessions HTTP ?

Les sessions HTTP permettent aux serveurs Web de conserver l'identité de l'utilisateur et de stocker des données spécifiques à l'utilisateur à travers plusieurs interactions requête/réponse entre une application cliente et une application Web. Lorsqu'un client se connecte à l'application, le serveur génère un SessionID. La session est enregistrée en mémoire à l'aide d'un mécanisme de stockage persistant non répliqué sur un seul serveur. Des exemples de tels mécanismes incluent la persistance JDBC, la persistance du système de fichiers, la persistance de session basée sur les cookies et la réplication en mémoire. Lorsque l'utilisateur envoie une demande ultérieure, l'ID de session est transmis dans l'en-tête de la demande et le navigateur vérifie si l'ID correspond à l'un des éléments de la mémoire de stockage et accorde l'accès à l'utilisateur jusqu'à l'expiration de la session.

Les sessions HTTP stockent les données suivantes en mémoire :

  • Spécificités de la session (identifiant de session, heure de création, heure du dernier accès, etc.)
  • Informations contextuelles sur l'utilisateur (par exemple, l'état de connexion du client)

Qu'est-ce que Redis ?

Redis (Remote Dictionary Server) est un magasin de données clé-valeur rapide, open source et en mémoire utilisé comme base de données, cache, courtier de messages et file d'attente.

Redis a des temps de réponse inférieurs à la milliseconde, permettant des millions de requêtes par seconde pour des applications en temps réel dans des secteurs tels que les jeux, la technologie publicitaire, la finance, la santé et l'IoT. En conséquence, Redis est désormais l'un des moteurs open source les plus populaires, ayant été nommé la base de données "Most Loved" par Stack Overflow cinq années de suite. En raison de ses performances rapides, Redis est un choix populaire pour la mise en cache, la gestion de session, les jeux, les classements, l'analyse en temps réel, la géospatiale, le covoiturage, le chat/messagerie, le streaming multimédia et les pub/sous-applications.

Que construisons-nous ?

Pour démontrer la gestion de session dans Node.js, nous allons créer une application simple d'inscription et de connexion. Les utilisateurs s'inscriront et se connecteront à cette application en fournissant leur adresse e-mail et leur mot de passe. Une session est créée et enregistrée dans le magasin Redis pour les demandes futures lorsqu'un utilisateur se connecte. Lorsqu'un utilisateur se déconnecte, nous supprimons sa session. Assez parlé ; commençons !

Prérequis

Ce tutoriel est une démonstration pratique. Assurez-vous que les éléments suivants sont installés avant de commencer :

  • Node.js
  • CLI Redis
  • Base de données MySQL
  • Arctype

Le code de ce tutoriel est disponible sur mon dépôt Github. N'hésitez pas à cloner et à suivre.

Configuration du projet

Commençons par créer un dossier de projet pour l'application avec la commande ci-dessous :

mkdir Session_management && cd Session_management

Ensuite, initialisez une application Node.js pour créer un fichier package.json avec la commande ci-dessous :

npm init -y

Le -y flag dans la commande ci-dessus indique à npm d'utiliser la configuration par défaut. Créez maintenant la structure de dossiers suivante dans le répertoire racine de votre projet.

Une fois notre package.json créé, installons le package requis pour ce projet dans la section suivante.

Installer les dépendances

Nous allons installer les dépendances suivantes pour notre application :

  • Bcryptjs - Ce module sera utilisé pour hacher le mot de passe de l'utilisateur.
  • Connect-redis - Ce module fournira un stockage de session Redis pour Express.
  • Session express - Ce module sera utilisé pour créer des sessions.
  • Ejs - Ce module est notre moteur de template
  • Passeport - Ce module sera utilisé pour l'authentification de l'utilisateur
  • Passeport local - Ce module sera utilisé pour l'authentification locale du nom d'utilisateur et du mot de passe
  • Séqueliser - Ce module est notre ORM MySQL pour connecter notre application à la base de données MySQL.
  • Dotenv - Ce module sera utilisé pour charger nos variables d'environnement.

Utilisez la commande ci-dessous pour installer toutes les dépendances requises.

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

Attendez que l'installation se termine. Une fois l'installation terminée, procédez à la configuration de la base de données MySQL dans la section suivante.

Configuration de la base de données MySQL

Nous allons créer une base de données MySQL pour notre application. Mais avant cela, exécutez la commande ci-dessous pour créer un compte utilisateur MySQL.

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

Créez maintenant une base de données session_db et accordez au nouvel utilisateur l'accès à la base de données avec la commande ci-dessous :

#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';

Rechargez maintenant tous les privilèges avec la commande ci-dessous :

FLUSH PRIVILEGES;

Avec notre configuration de base de données MySQL, créons nos users modèle de base de données dans la section suivante.

Créer un serveur express

Avec notre configuration de base de données MySQL, créons un serveur express pour notre application. Ouvrez le fichier src/server.js et ajoutez l'extrait de code ci-dessous :

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}`);
});

Dans l'extrait de code ci-dessus, nous créons un serveur express, qui écoutera les requêtes sur le port 4300. Ensuite, nous analysons les requêtes entrantes avec des charges utiles JSON à l'aide de express.json() intergiciel et analyse les requêtes entrantes avec urlencoded en utilisant Express.urlencoded() middleware.

Créer le modèle de base de données

À ce stade, notre serveur Express est configuré. Nous allons maintenant créer un Users modèle pour représenter les données utilisateur, nous verrons la base de données en utilisant Sequelize . Ouvrez le src/models/index.js fichier et ajoutez l'extrait de code ci-dessous.

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,
 },
});

Dans l'extrait de code ci-dessus, nous importons Sequelize et DateTypes de sequelize pour se connecter à notre base de données MySQL et attribuer un type de données à nos propriétés de modèle. Ensuite, on se connecte à MySQL en créant un sequelize instance de Sequelize class et en transmettant nos informations d'identification de base de données. Par exemple, avec le sequelize Par exemple, nous avons défini notre modèle et ses propriétés. Nous ne voulons que les champs d'identifiant, d'e-mail et de mot de passe de ce didacticiel. Mais sequelize crée deux champs supplémentaires, le createdAt , et updatedAt champs.

Configurer Passport et Redis

Pour gérer et stocker les identifiants de nos utilisateurs, nous utiliserons et configurerons Redis . Pour ce faire, ouvrez le src/index.js fichier et importez les dépendances suivantes ci-dessous :

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

Ensuite, repérez la zone commentée //Redis configurations et ajoutez l'extrait de code ci-dessous :

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

Dans l'extrait de code ci-dessus, nous avons établi une connexion à notre base de données, qui gérera les données de nom d'utilisateur de notre utilisateur.

Ensuite, repérez la zone commentée //Commented session middleware et ajoutez l'extrait de code ci-dessous :

//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());

Dans l'extrait de code ci-dessus, nous avons créé un SESSION_SECRET variable dans un .env fichier pour garder notre session secrète, puis créé un middleware de session et utilisé Redis comme magasin. Pour que la session fonctionne, nous ajoutons deux middlewares supplémentaires, le passport.initialize() , et passport.session() .

Créer des contrôleurs d'application

Avec notre configuration de session Redis et express, nous allons créer un itinéraire pour gérer les informations des utilisateurs. Pour ce faire, ouvrez le src/controllers/index.js fichier et ajoutez l'extrait de code ci-dessous :

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);
 }
};

Dans l'extrait de code ci-dessus, nous importons bcrypt et notre User modèle, nous déstructurons l'email de l'utilisateur et password depuis le req.body objet. Ensuite, nous avons haché le mot de passe en utilisant bcrypt et créons un nouvel utilisateur en utilisant le sequelize create méthode.

Ensuite, créez une home page , registration page , login page avec le bout de code ci-dessous :

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");
};

Dans la home page , nous afficherons certains des détails de l'utilisateur authentifié à côté du home vue.

Enfin, créez le logout route, pour supprimer les données de nom d'utilisateur de l'utilisateur avec l'extrait de code ci-dessous :

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

Créer la stratégie Passeport

À ce stade, les utilisateurs peuvent s'inscrire, se connecter et se déconnecter de notre application. Créons maintenant la stratégie de passeport pour authentifier les utilisateurs et créer une session. Pour ce faire, ouvrez le src/utils/passport.js fichier et ajoutez l'extrait de code ci-dessous :

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);
 });
};

Dans l'extrait de code ci-dessus, nous importons passport , bcrypt , et notre modèle User, et nous créons un middleware de passeport pour utiliser la local-strategy . Ensuite, nous renommons le nom de fichier par défaut en noms de champs ( email , password ) que nous utilisons pour authentifier les utilisateurs. Maintenant, nous vérifions si les détails de l'utilisateur existent dans la base de données avant qu'une session puisse être créée pour eux.

Le Passport.serialize et passport.deserialize Les commandes sont utilisées pour conserver l'identifiant de l'utilisateur en tant que cookie dans le navigateur de l'utilisateur et pour récupérer l'identifiant du cookie si nécessaire, qui est ensuite utilisé pour récupérer les informations de l'utilisateur dans un rappel.

Le done() la fonction est un passport.js interne fonction qui prend l'identifiant de l'utilisateur comme second paramètre.

Créer les routes de l'application

Une fois notre stratégie de passeport créée, procédons à la création d'itinéraires pour nos contrôleurs. Pour ce faire, ouvrez le src/routes/index.js fichier et ajoutez l'extrait de code suivant ci-dessous :

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;

Dans l'extrait de code ci-dessus, nous importons nos fonctions de contrôleur et créons une route pour elles. Pour la signin route , nous avons utilisé le passport.authenticate méthode pour authentifier les utilisateurs en utilisant le local stratégie dans la configuration de la section précédente.

Revenons maintenant à notre server.js fichier, nous allons créer un middleware pour nos routes. Avant cela, nous devons importer notre router et le passportConfig une fonction.

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

Ensuite, nous appellerons le passportConfig fonction juste en dessous du code dans les zones commentées //Configure session middleware .

passportConfig();

Ensuite, nous créerons notre middleware de route juste après la zone commentée //Router middleware .

app.use(router);

Créer nos vues d'application

Une fois nos routes créées, nous créerons des vues rendues dans notre HomePage , LoginPage , et RegisterPage contrôleurs. Avant cela, nous allons configurer notre moteur de vue ejs dans le fichier server.js avec un extrait de code ci-dessous juste en dessous de la zone commentée //app middleware .

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

Ensuite, nous allons commencer par la page d'accueil, ouvrir le views/home.ejs fichier et ajoutez le balisage suivant.

<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>

Ici, sur notre page d'accueil, nous avons utilisé bootstrap pour ajouter du style à nos balises. Ensuite, nous vérifions si l'utilisateur est authentifié pour afficher le bouton de déconnexion. Nous affichons également l'Email de l'utilisateur , sessionID , et ExpirationTime depuis le backend.

Ensuite, ouvrez le src/views/auth/resgister et ajoutez le balisage suivant ci-dessous pour la page de registre.

<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>

Dans la page d'enregistrement, nous avons créé un formulaire html pour accepter les détails des utilisateurs. Dans le formulaire, nous ajoutons également l'attribut active et spécifions le point de terminaison d'inscription. Cela signifie que lorsqu'un utilisateur clique sur le bouton Soumettre, une demande sera envoyée à /api/v1/signup point de terminaison.

Enfin, ouvrez le src/views/auth/signin.js fichier et ajoutez l'extrait de balisage suivant ci-dessous :

<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>

Dans le balisage ci-dessus, nous avons ajouté un formulaire html qui sera utilisé pour connecter un utilisateur en envoyant une requête au /api/v1/signin point de terminaison.

Afficher les données des utilisateurs avec Arctype

Nous avons maintenant créé avec succès une application de gestion de session Node.js. Regardons les données des utilisateurs avec Arctype. Pour commencer, lancez Arctype, cliquez sur l'onglet MySQL et entrez les informations d'identification MySQL suivantes, comme indiqué dans la capture d'écran ci-dessous :

Ensuite, cliquez sur les users tableau pour afficher les utilisateurs enregistrés comme indiqué dans la capture d'écran ci-dessous :

Conclusion

En créant une application de connexion de démonstration, nous avons appris à implémenter la gestion de session dans Node.js à l'aide de Passport et Redis. Nous avons commencé par l'introduction des sessions HTTP et leur fonctionnement, puis nous avons regardé ce qu'est Redis, et créé un projet pour mettre tout cela en pratique. Maintenant que vous avez les connaissances recherchées, comment authentifierez-vous les projets des utilisateurs ?