sql >> Database >  >> RDS >> Mysql

Sessies beheren in Node.js met Passport, Redis en MySQL

HTTP en HTTPS zijn internetprotocollen waarmee gegevens via internet kunnen worden verzonden door een verzoek via een webbrowser te verzenden. Omdat ze stateless zijn, wordt elk verzoek dat naar de browser wordt verzonden, onafhankelijk behandeld. Dit betekent dat de browser de bron van een verzoek niet kan onthouden, zelfs niet als dezelfde gebruiker het doet. HTTP-sessies lossen dit probleem op.

Dit artikel gaat in op sessiebeheer en hoe tools zoals Passport, Redis en MySQL ons kunnen helpen bij het beheren van Node.js-sessies. Laten we erin duiken.

Hoe werken HTTP-sessies?

HTTP-sessies stellen webservers in staat om de gebruikersidentiteit te behouden en gebruikersspecifieke gegevens op te slaan over meerdere verzoek-/antwoordinteracties tussen een client-app en een web-app. Wanneer een client zich aanmeldt bij de toepassing, genereert de server een SessionID. De sessie wordt in het geheugen opgeslagen met behulp van een niet-gerepliceerd permanent opslagmechanisme op één server. Voorbeelden van dergelijke mechanismen zijn JDBC-persistentie, bestandssysteempersistentie, op cookies gebaseerde sessiepersistentie en replicatie in het geheugen. Wanneer de gebruiker een volgend verzoek verzendt, wordt de sessionID doorgegeven in de verzoekheader en controleert de browser of de ID overeenkomt met een van de in de geheugenopslag en verleent de gebruiker toegang totdat de sessie verloopt.

HTTP-sessies slaan de volgende gegevens op in het geheugen:

  • Specificaties over de sessie (sessie-ID, aanmaaktijd, laatste keer geopend, enz.)
  • Contextuele informatie over de gebruiker (bijvoorbeeld de aanmeldingsstatus van de klant)

Wat is Redis?

Redis (Remote Dictionary Server) is een snelle, open-source, in-memory key-value datastore die wordt gebruikt als database, cache, berichtenmakelaar en wachtrij.

Redis heeft responstijden van minder dan een milliseconde, waardoor miljoenen verzoeken per seconde mogelijk zijn voor realtime toepassingen in sectoren zoals gaming, ad-tech, financiën, gezondheidszorg en IoT. Als gevolg hiervan is Redis nu een van de meest populaire open-source-engines, die vijf jaar op rij door Stack Overflow is uitgeroepen tot de "Most Loved"-database. Vanwege de snelle prestaties is Redis een populaire keuze voor caching, sessiebeheer, gaming, leaderboards, realtime analyses, geospatial, ride-hailing, chat/messaging, mediastreaming en pub/sub-apps.

Wat zijn we aan het bouwen?

Om sessiebeheer in Node.js te demonstreren, zullen we een eenvoudige aanmeldings- en aanmeldingstoepassing maken. Gebruikers zullen zich aanmelden voor en inloggen op deze applicatie door hun e-mailadres en wachtwoord op te geven. Er wordt een sessie gemaakt en opgeslagen in de Redis-winkel voor toekomstige verzoeken wanneer een gebruiker zich aanmeldt. Wanneer een gebruiker uitlogt, zullen we zijn sessie verwijderen. Genoeg gepraat; laten we beginnen!

Vereisten

Deze tutorial is een praktische demonstratie. Zorg ervoor dat u het volgende hebt geïnstalleerd voordat u aan de slag gaat:

  • Node.js
  • Redis CLI
  • MySQL-database
  • Arctype

De code voor deze tutorial is beschikbaar op mijn Github-repository. Voel om te klonen en volg mee.

Projectconfiguratie

Laten we beginnen met het maken van een projectmap voor de applicatie met de onderstaande opdracht:

mkdir Session_management && cd Session_management

Initialiseer vervolgens een Node.js-toepassing om een ​​package.json-bestand te maken met de onderstaande opdracht:

npm init -y

De -y vlag in de bovenstaande opdracht vertelt npm om de standaardconfiguratie te gebruiken. Maak nu de volgende mappenstructuur in de hoofdmap van uw project.

Nu ons package.json is gemaakt, laten we het vereiste pakket voor dit project in de volgende sectie installeren.

Afhankelijkheden installeren

We zullen de volgende afhankelijkheden voor onze applicatie installeren:

  • Bcryptjs - Deze module wordt gebruikt om het wachtwoord van de gebruiker te hashen.
  • Connect-redis - Deze module biedt Redis-sessieopslag voor Express.
  • Express-sessie - Deze module wordt gebruikt om sessies te maken.
  • Ejs - Deze module is onze template engine
  • Paspoort - Deze module wordt gebruikt voor gebruikersauthenticatie
  • Paspoort-lokaal - Deze module wordt gebruikt voor lokale gebruikersnaam en wachtwoordverificatie
  • Vervolg - Deze module is onze MySQL ORM om onze applicatie te verbinden met de MySQL-database.
  • Dotenv - Deze module wordt gebruikt om onze omgevingsvariabelen te laden.

Gebruik de onderstaande opdracht om alle vereiste afhankelijkheden te installeren.

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

Wacht tot de installatie is voltooid. Zodra de installatie is voltooid, gaat u verder met het instellen van de MySQL-database in de volgende sectie.

MySQL-database instellen

We zullen een MySQL-database maken voor onze applicatie. Maar voer eerst de onderstaande opdracht uit om een ​​MySQL-gebruikersaccount aan te maken.

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

Maak nu een database session_db en verleen de nieuwegebruiker toegang tot de database met het onderstaande commando:

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

Laad nu alle privileges opnieuw met de onderstaande opdracht:

FLUSH PRIVILEGES;

Laten we met onze MySQL-databaseconfiguratie onze users . maken databasemodel in de volgende sectie.

Express-server maken

Laten we met onze MySQL-databaseconfiguratie een expressserver voor onze applicatie maken. Open het bestand src/server.js en voeg het onderstaande codefragment toe:

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

In het bovenstaande codefragment maken we een express-server die luistert naar verzoeken op poort 4300. Vervolgens parsen we de inkomende verzoeken met JSON-payloads met behulp van de express.json() middleware en ontleden inkomende verzoeken met urlencoded met behulp van Express.urlencoded() middleware.

Maak het databasemodel

Op dit punt is onze Express-server ingesteld. Nu maken we een Users model om de gebruikersgegevens weer te geven, we zullen de database zien met behulp van Sequelize . Open de src/models/index.js bestand en voeg het onderstaande codefragment toe.

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

In het bovenstaande codefragment importeren we Sequelize en DateTypes van sequelize om verbinding te maken met onze MySQL-database en een gegevenstype toe te wijzen aan onze modeleigenschappen. Vervolgens maken we verbinding met MySQL door een sequelize . te maken instantie uit de Sequelize klasse en het doorgeven van onze databasereferenties. Bijvoorbeeld met de sequelize we hebben bijvoorbeeld ons model en zijn eigenschappen gedefinieerd. We willen alleen de id-, e-mail- en wachtwoordvelden van deze tutorial. Maar sequelize creëert twee extra velden, de createdAt , en updatedAt velden.

Paspoort en Redis instellen

Om de inloggegevens van onze gebruikers te verwerken en op te slaan, gebruiken en configureren we Redis . Open hiervoor de src/index.js bestand en importeer de volgende afhankelijkheden hieronder:

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

Zoek vervolgens het gebied met commentaar //Redis configurations en voeg het onderstaande codefragment toe:

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

In het bovenstaande codefragment hebben we een verbinding tot stand gebracht met onze database, die de gebruikersnaamgegevens van onze gebruikers zal beheren.

Zoek vervolgens het gebied met commentaar //Commented session middleware en voeg het onderstaande codefragment toe:

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

In het bovenstaande codefragment hebben we een SESSION_SECRET . gemaakt variabele in een .env bestand om onze sessie geheim te houden, maakte vervolgens een sessie-middleware en gebruikte Redis als onze winkel. Om de sessie te laten werken, voegen we nog twee middlewares toe, de passport.initialize() , en passport.session() .

Applicatiecontrollers maken

Met onze Redis- en express-sessieconfiguratie creëren we een route om gebruikersinformatie te verwerken. Open hiervoor de src/controllers/index.js bestand en voeg het onderstaande codefragment toe:

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

In het bovenstaande codefragment importeren we bcrypt en onze User model, we destructureren de email van de gebruiker en password van de req.body voorwerp. Vervolgens hebben we het wachtwoord gehasht met behulp van bcrypt en een nieuwe gebruiker gemaakt met behulp van de sequelize create methode.

Maak vervolgens een home page , registration page , login page met onderstaand codefragment:

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

Op de HomePage , geven we enkele gegevens van de geverifieerde gebruiker weer naast de home bekijken.

Maak ten slotte de logout route, om de gebruikersnaamgegevens van de gebruiker te verwijderen met het onderstaande codefragment:

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

Maak de paspoortstrategie

Op dit punt kunnen gebruikers zich registreren, inloggen en uitloggen bij onze applicatie. Laten we nu de paspoortstrategie maken om de gebruikers te authenticeren en een sessie te creëren. Open hiervoor de src/utils/passport.js bestand en voeg het onderstaande codefragment toe:

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

In het bovenstaande codefragment importeren we passport , bcrypt , en ons gebruikersmodel, en we maken een paspoort-middleware om de local-strategy te gebruiken . Daarna hernoemen we de standaard bestandsnaam naar de veldnamen ( email , password ) die we gebruiken om de gebruikers te authenticeren. Nu controleren we of de gebruikersgegevens in de database bestaan ​​voordat er een sessie voor hen kan worden aangemaakt.

De Passport.serialize en passport.deserialize commando's worden gebruikt om de gebruikers-ID als een cookie in de browser van de gebruiker te bewaren en om de ID indien nodig uit de cookie op te halen, die vervolgens wordt gebruikt om gebruikersinformatie op te halen in een callback.

De done() functie is een intern passport.js functie die de gebruikers-ID als tweede parameter neemt.

Maak de aanvraagroutes

Nu onze paspoortstrategie is gemaakt, gaan we verder met het maken van routes voor onze controllers. Open hiervoor de src/routes/index.js bestand en voeg het volgende codefragment hieronder toe:

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;

In het bovenstaande codefragment importeren we onze controllerfuncties en hebben we er een route voor gemaakt. Voor de signin route ,we gebruikten de passport.authenticate methode om de gebruikers te authenticeren met behulp van de local strategie in setup in de vorige sectie.

Nu terug naar onze server.js bestand, maken we een middleware voor onze routes. Daarvoor moeten we onze router . importeren en de passportConfig functie.

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

Vervolgens bellen we de passportConfig functie direct onder de code in de gebieden met commentaar //Configure session middleware .

passportConfig();

Vervolgens maken we onze route-middleware direct na het gebied met commentaar//Router middleware .

app.use(router);

Maak onze applicatieweergaven

Met onze routes gemaakt, zullen we weergaven maken die worden weergegeven in onze HomePage , LoginPage , en RegisterPage controleurs. Daarvoor stellen we onze ejs-weergave-engine in het server.js-bestand in met een codefragment hieronder, direct onder het gebied met commentaar //app middleware .

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

Vervolgens beginnen we met de startpagina, openen de views/home.ejs bestand en voeg de volgende opmaak toe.

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

Hier op onze startpagina hebben we bootstrap gebruikt om wat styling aan onze markeringen toe te voegen. Vervolgens controleren we of de gebruiker is geauthenticeerd om de uitlogknop te tonen. We tonen ook de Email . van de gebruiker , sessionID , en ExpirationTime vanaf de backend.

Open vervolgens de src/views/auth/resgister en voeg de volgende opmaak hieronder toe voor de registerpagina.

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

Op de registratiepagina hebben we een html-formulier gemaakt om de gebruikersgegevens te accepteren. In het formulier voegen we ook het actieve kenmerk toe en specificeren we het aanmeldingseindpunt. Dit betekent dat wanneer een gebruiker op de verzendknop klikt, een verzoek wordt verzonden naar de /api/v1/signup eindpunt.

Open ten slotte de src/views/auth/signin.js bestand en voeg het volgende markup-fragment hieronder toe:

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

In de bovenstaande opmaak hebben we een html-formulier toegevoegd dat zal worden gebruikt om een ​​gebruiker in te loggen door een verzoek te sturen naar de /api/v1/signin eindpunt.

Gegevens van gebruikers bekijken met Arctype

We hebben nu met succes een Node.js-sessiebeheertoepassing gemaakt. Laten we eens kijken naar de gebruikersgegevens met Arctype. Start om te beginnen Arctype, klik op het tabblad MySQL en voer de volgende MySQL-inloggegevens in, zoals weergegeven in de onderstaande schermafbeelding:

Klik vervolgens op de users tabel om de geregistreerde gebruikers te tonen zoals getoond in de onderstaande schermafbeelding:

Conclusie

Door een demo-inlogtoepassing te bouwen, hebben we geleerd hoe we sessiebeheer in Node.js kunnen implementeren met Passport en Redis. We zijn begonnen met de introductie van HTTP-sessies en hoe ze werken, daarna hebben we gekeken naar wat Redis is en hebben we een project opgezet om dit allemaal in de praktijk te brengen. Nu u de kennis heeft die u zoekt, hoe zou u de projecten van gebruikers verifiëren?


  1. Bewaar procedures in phpMyAdmin

  2. UUID of SEQUENTIE voor primaire sleutel?

  3. LEFT() vs SUBSTRING() in SQL Server:wat is het verschil?

  4. MySQL invoegen vanuit de ene database in een andere