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 page
、login 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
を使用するパスポートミドルウェアを作成します 。次に、デフォルトのファイル名をフィールド名に変更します(email
、password
)ユーザーの認証に使用しています。ここで、セッションを作成する前に、ユーザーの詳細がデータベースに存在するかどうかを確認します。
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とは何かを調べ、これらすべてを実践するためのプロジェクトを作成しました。求めている知識が得られたので、ユーザーのプロジェクトをどのように認証しますか?