sql >> データベース >  >> RDS >> Mysql

Passport、Redis、およびMySQLを使用してNode.jsでセッションを管理する方法

    HTTPおよびHTTPSは、Webブラウザーを介して要求を送信することにより、インターネットを介してデータを送信できるようにするインターネットプロトコルです。これらはステートレスであるため、ブラウザに送信される各リクエストは個別に処理されます。これは、同じユーザーがリクエストを作成した場合でも、ブラウザがリクエストのソースを記憶できないことを意味します。 HTTPセッションはこの問題を解決します。

    この記事では、セッション管理と、Passport、Redis、MySQLなどのツールがNode.jsセッションの管理にどのように役立つかについて説明します。飛び込みましょう。

    HTTPセッションはどのように機能しますか?

    HTTPセッションを使用すると、WebサーバーはユーザーIDを維持し、クライアントアプリとWebアプリ間の複数の要求/応答の相互作用にわたってユーザー固有のデータを保存できます。クライアントがアプリケーションにログインすると、サーバーはSessionIDを生成します。セッションは、単一サーバーの複製されていない永続ストレージメカニズムを使用してメモリに保存されます。このようなメカニズムの例には、JDBC永続性、ファイルシステム永続性、Cookieベースのセッション永続性、およびメモリ内レプリケーションが含まれます。ユーザーが後続のリクエストを送信すると、sessionIDがリクエストヘッダーで渡され、ブラウザはIDがメモリストレージ内のいずれかと一致するかどうかを確認し、セッションが期限切れになるまでユーザーにアクセスを許可します。

    HTTPセッションは、次のデータをメモリに保存します。

    • セッションの詳細(セッション識別子、作成時間、最後にアクセスした時間など)
    • ユーザーに関するコンテキスト情報(クライアントのログインステータスなど)

    Redisとは何ですか?

    Redis(Remote Dictionary Server)は、データベース、キャッシュ、メッセージブローカー、およびキューとして使用される、高速でオープンソースのメモリ内のKey-Valueデータストアです。

    Redisの応答時間はミリ秒未満であり、ゲーム、アドテク、金融、ヘルスケア、IoTなどの業界のリアルタイムアプリケーションに対して、1秒あたり数百万のリクエストを許可します。その結果、Redisは現在最も人気のあるオープンソースエンジンの1つであり、StackOverflowによって5年連続で「最も愛されている」データベースに選ばれています。パフォーマンスが速いため、Redisは、キャッシュ、セッション管理、ゲーム、リーダーボード、リアルタイム分析、地理空間、ライドヘイリング、チャット/メッセージング、メディアストリーミング、パブ/サブアプリで人気があります。

    何を構築していますか?

    Node.jsでのセッション管理を示すために、簡単なサインアップおよびサインインアプリケーションを作成します。ユーザーは、自分の電子メールアドレスとパスワードを入力して、このアプリケーションにサインアップしてサインインします。セッションが作成され、ユーザーがサインインしたときの今後のリクエストのためにRedisストアに保存されます。ユーザーがログアウトすると、セッションが削除されます。十分に話します。始めましょう!

    前提条件

    このチュートリアルは実践的なデモンストレーションです。開始する前に、次のものがインストールされていることを確認してください。

    • Node.js
    • Redis CLI
    • MySQLデータベース
    • Arctype

    このチュートリアルのコードは、私のGithubリポジトリで入手できます。クローンを作成してフォローしてください。

    プロジェクトの設定

    以下のコマンドを使用して、アプリケーションのプロジェクトフォルダを作成することから始めましょう。

    mkdir Session_management && cd Session_management
    

    次に、Node.jsアプリケーションを初期化して、以下のコマンドでpackage.jsonファイルを作成します。

    npm init -y
    

    -y 上記のコマンドのフラグは、デフォルト構成を使用するようにnpmに指示します。次に、プロジェクトのルートディレクトリに次のフォルダ構造を作成します。

    package.jsonを作成したら、次のセクションでこのプロジェクトに必要なパッケージをインストールしましょう。

    依存関係のインストール

    アプリケーションに次の依存関係をインストールします:

    • Bcryptjs -このモジュールは、ユーザーのパスワードをハッシュするために使用されます。
    • Connect-redis -このモジュールは、Express用のRedisセッションストレージを提供します。
    • エクスプレスセッション -このモジュールは、セッションの作成に使用されます。
    • Ejs -このモジュールはテンプレートエンジンです
    • パスポート -このモジュールはユーザーの認証に使用されます
    • パスポート-ローカル -このモジュールは、ローカルのユーザー名とパスワードの認証に使用されます
    • 続編 -このモジュールは、アプリケーションをMySQLデータベースに接続するためのMySQLORMです。
    • Dotenv -このモジュールは、環境変数をロードするために使用されます。

    以下のコマンドを使用して、必要なすべての依存関係をインストールします。

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

    インストールが完了するのを待ちます。インストールが完了したら、次のセクションでMySQLデータベースのセットアップに進みます。

    MySQLデータベースのセットアップ

    アプリケーション用のMySQLデータベースを作成します。ただし、その前に、以下のコマンドを実行してMySQLユーザーアカウントを作成します。

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

    次に、データベースsession_dbを作成し、次のコマンドを使用して、newuserにデータベースへのアクセスを許可します。

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

    次に、以下のコマンドを使用してすべての特権を再ロードします。

    FLUSH PRIVILEGES;
    

    MySQLデータベースのセットアップで、usersを作成しましょう 次のセクションのデータベースモデル。

    Expressサーバーの作成

    MySQLデータベースのセットアップで、アプリケーション用のエクスプレスサーバーを作成しましょう。 src / server.jsファイルを開き、以下のコードスニペットを追加します。

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

    上記のコードスニペットでは、ポート4300でリクエストをリッスンするエクスプレスサーバーを作成します。次に、express.json()を使用して、JSONペイロードで着信リクエストを解析します。 ミドルウェアとurlencodedを使用した着信リクエストの解析 Express.urlencoded()を使用する ミドルウェア。

    データベースモデルを作成する

    この時点で、Expressサーバーが設定されています。次に、usersを作成します ユーザーデータを表すモデルSequelizeを使用してデータベースを表示します 。 src/models/index.jsを開きます ファイルを作成し、以下のコードスニペットを追加します。

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

    上記のコードスニペットでは、Sequelizeをインポートします およびDateTypes sequelizeから MySQLデータベースに接続し、モデルプロパティにデータ型を割り当てます。次に、sequelizeを作成してMySQLに接続します Sequelizeからのインスタンス クラスとデータベースのクレデンシャルを渡します。たとえば、sequelize たとえば、モデルとそのプロパティを定義しました。このチュートリアルのID、電子メール、およびパスワードのフィールドのみが必要です。ただし、sequelizeは、createdAtという2つの追加フィールドを作成します。 、およびupdatedAt フィールド。

    パスポートとRedisのセットアップ

    ユーザーの資格情報を処理および保存するために、Redisを使用および構成します 。これを行うには、src/index.jsを開きます 以下の依存関係をファイルしてインポートします。

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

    次に、コメントされた//Redis configurationsの領域を見つけます 以下のコードスニペットを追加します。

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

    上記のコードスニペットでは、ユーザーのユーザー名データを管理するデータベースへの接続を確立しました。

    次に、コメントされた領域を見つけます//Commented session middleware 以下のコードスニペットを追加します。

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

    上記のコードスニペットでは、SESSION_SECRETを作成しました .envの変数 セッションシークレットを保持するファイルを作成し、セッションミドルウェアを作成してRedisをストアとして使用しました。セッションを機能させるために、さらに2つのミドルウェアpassport.initialize()を追加します。 、およびpassport.session()

    アプリケーションコントローラーを作成する

    Redisとエクスプレスセッションのセットアップを使用して、ユーザーの情報を処理するためのルートを作成します。これを行うには、src/controllers/index.jsを開きます ファイルを作成し、以下のコードスニペットを追加します。

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

    上記のコードスニペットでは、bcryptをインポートします と私たちのusers モデルでは、ユーザーのemailを分解します およびpassword req.bodyから 物体。次に、bcryptを使用してパスワードをハッシュし、sequelize createを使用して新しいユーザーを作成しました メソッド。

    次に、home pageを作成します 、registration pagelogin page 以下のコードスニペットを使用:

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

    home page 、認証されたユーザーの詳細の一部をhomeと一緒にレンダリングします ビュー。

    最後に、logoutを作成します ルート、以下のコードスニペットを使用してユーザーのユーザー名データを削除します。

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

    パスポート戦略を作成する

    この時点で、ユーザーはアプリケーションの登録、ログイン、およびログアウトを行うことができます。それでは、ユーザーを認証してセッションを作成するためのパスポート戦略を作成しましょう。これを行うには、src/utils/passport.jsを開きます ファイルを作成し、以下のコードスニペットを追加します。

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

    上記のコードスニペットでは、passportをインポートします 、bcrypt 、およびユーザーモデル、およびlocal-strategyを使用するパスポートミドルウェアを作成します 。次に、デフォルトのファイル名をフィールド名に変更します(emailpassword )ユーザーの認証に使用しています。ここで、セッションを作成する前に、ユーザーの詳細がデータベースに存在するかどうかを確認します。

    Passport.serialize およびpassport.deserialize コマンドは、ユーザーのブラウザでユーザーIDをCookieとして保持し、必要に応じてCookieからIDを取得するために使用されます。その後、コールバックでユーザー情報を取得するために使用されます。

    done() 関数は内部のpassport.jsです ユーザーIDを2番目のパラメーターとして受け取る関数。

    アプリケーションルートを作成する

    パスポート戦略を作成したら、コントローラーのルートの作成に進みましょう。これを行うには、src/routes/index.jsを開きます ファイルを作成し、以下のコードスニペットを追加します。

    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;
    

    上記のコードスニペットでは、コントローラー関数をインポートして、それらのルートを作成しました。 signin routeの場合 、passport.authenticateを使用しました localを使用してユーザーを認証する方法 前のセクションのセットアップの戦略。

    ここで、server.jsに戻ります。 ファイル、ルートのミドルウェアを作成します。その前に、routerをインポートする必要があります およびpassportConfig 働き。

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

    次に、passportConfigを呼び出します。 コメントされた領域のコードのすぐ下にある機能//Configure session middleware

    passportConfig();
    

    次に、エリアがコメントした直後にルートミドルウェアを作成します//Router middleware

    app.use(router);
    

    アプリケーションビューを作成する

    ルートを作成したら、home pageでレンダリングされたビューを作成します 、LoginPage 、およびRegisterPage コントローラー。その前に、server.jsファイルにejsビューエンジンを設定し、コメントされた領域のすぐ下にあるコードスニペットを使用して//app middleware

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

    次に、ホームページから始めて、views/home.ejsを開きます。 ファイルを作成し、次のマークアップを追加します。

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

    ここのホームページでは、ブートストラップを使用してマークアップにスタイルを追加しました。次に、ユーザーが認証されてログアウトボタンが表示されるかどうかを確認します。また、ユーザーのEmailを表示します 、sessionID 、およびExpirationTime バックエンドから。

    次に、src/views/auth/resgisterを開きます 登録ページに次のマークアップを追加します。

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

    登録ページでは、ユーザーの詳細を受け入れるためのhtmlフォームを作成しました。フォームでは、アクティブな属性を追加し、サインアップエンドポイントも指定します。これは、ユーザーが送信ボタンをクリックすると、リクエストが/api/v1/signupに送信されることを意味します エンドポイント。

    最後に、src/views/auth/signin.jsを開きます ファイルを作成し、以下のマークアップスニペットを追加します。

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

    上記のマークアップでは、/api/v1/signinにリクエストを送信して、ユーザーのログインに使用するhtmlフォームを追加しました。 エンドポイント。

    Arctypeでユーザーのデータを表示する

    これで、Node.jsセッション管理アプリケーションが正常に作成されました。 Arctypeを使用してユーザーのデータを見てみましょう。開始するには、Arctypeを起動し、[MySQL]タブをクリックして、次のスクリーンショットに示すように、次のMySQLクレデンシャルを入力します。

    次に、usersをクリックします 以下のスクリーンショットに示すように、登録済みユーザーを表示する表:

    結論

    デモログインアプリケーションを構築することで、PassportとRedisを使用してNode.jsでセッション管理を実装する方法を学びました。まず、HTTPセッションとその仕組みの紹介から始め、次にRedisとは何かを調べ、これらすべてを実践するためのプロジェクトを作成しました。求めている知識が得られたので、ユーザーのプロジェクトをどのように認証しますか?


    1. OracleのCOS()関数

    2. SQL varchar内の特定のサブストリングの出現回数をどのようにカウントしますか?

    3. OracleCloudおよびAutonomousDatabaseでの無料認定試験の新しい日付

    4. なぜデータベースにビューを作成するのですか?