はじめに
React+ReduxアプリケーションにNode.js(express)でAPIを作り、jsonwebtoken(JWT)で認証・認可周りの処理を実装してみました。今回はAPI側の実装の内容を書いていきます。
使ったもの
- Node(v12.15.0)
- express(v4.17.1)
- jsonwebtoken(v8.5.1)
- body-parser(v1.19.1)
- nodemon(v2.0.3)
- REST Client(VSCodeの拡張機能)
ディレクトリ構成
root/
├ api/
│ ├ login.js
│ └ users.js
├ config/
│ └ jwt.config.js
├ middlewares/
│ └ verifyToken.js
├ app.js
└ request.rest
作るもの
[POST] /api/v1/login
ユーザーIDとパスワードを付与してリクエストすれば、認証が通ればjwtでトークンを返す
[GET] /api/v1/users
トークンをヘッダーに付与してリクエストすれば、ユーザー情報を返す
プロジェクト作成
プロジェクト作成して、必要なパッケージをインストールします。
npm init -y
npm install --save express jsonwebtoken body-parser nodemon
app.jsを作成
とりあえずexpressを読み込んで、appで初期化して、app.listenで起動する普通の流れです。
ルーティングは別ファイルにするので、express.Routerを初期化してリターンします。
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use("/api/v1",(() => {
const router = express.Router();
router.use("/login", require("./api/login.js"));
router.use("/users", require("./api/users.js"));
return router;
})()
);
app.listen(5000, function () {
console.log("Example app listening on port 5000!");
});
login.jsを作成
認証は本来はデータベースから行うが今回は割愛します。
const router = require("express").Router();
const jwt = require("jsonwebtoken");
const config = require("../config/jwt.config");
// POST /api/v1/login
router.post("/", (req, res) => {
// 本来はデータベースからユーザーIDとパスワードを認証するが割愛
if (
(req.body.userId == "001" && req.body.passWord == "qwerty") ||
(req.body.userId == "002" && req.body.passWord == "asdfgh")
) {
const payload = {
userId: req.body.userId,
};
const token = jwt.sign(payload, config.jwt.secret, config.jwt.options);
res.json({
isSuccess: true,
token: token,
});
} else {
res.json({
isSuccess: false,
message: "ユーザーIDまたはパスワードが違います。",
});
}
});
module.exports = router;
jwt.config.jsに設定を記載する。
module.exports = {
jwt: {
secret:
"cdfa54a89e0fbd4596213bf175336cfe05a78a73383f6dab39b8d374e7dceba53630546ff4c998b7bab9e53eaab2519a48e970b094315cf7472b811e45c1ea29",
options: {
algorithm: "HS256",
expiresIn: "20m",
},
},
};
シークレットキーはコマンドプロンプトで以下入力で取得したものです。
node
require("crypto").randomBytes(64).toString("hex")
ログインリクエストを試してみる
nodemonをインストールしてあれば以下で起動できます。
nodemon app.js
今回リクエストにはVSCodeの拡張機能の[REST Client]を使います。request.restファイルに以下のように記述すれば、
上部に[Send Request]が表示されるのでクリックすれば、リクエストできます。
POST http://localhost:5000/api/v1/login
Content-Type: application/json
{
"userId": "001",
"passWord": "qwerty"
}
認証に成功すれば以下レスポンスがあると思います。これでトークンが取得できましたで、ユーザー情報を取得します。
{
"isSuccess": true,
"token": "xxxxxxxxxxxxxxxxxxxx"
}
verifyToken.jsを作成
まずトークンを認証するミドルウェアを作成します。
リクエストヘッダーには以下のようにトークンを付与します。
Authorization: Bearer xxxxxxxxxxxxxxxxxxxx
ヘッダーからトークンを取得して認証処理を行います。
認証が成功したら、req.decodedにpayloadで設定した値が格納される。
const jwt = require("jsonwebtoken");
const config = require("../config/jwt.config");
function verifyToken(req, res, next) {
// splitで半角スペースで分割して後ろ側がTokenになる
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.split(" ")[1];
if (token) {
jwt.verify(token, config.jwt.secret, function (error, decoded) {
if (error) {
return res.status(403).send({
isSuccess: false,
message: "トークンの認証に失敗しました。",
});
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.status(401).send({
isSuccess: false,
message: "トークンがありません。",
});
}
}
module.exports = verifyToken;
users.jsを作成
こちらもは本来はデータベースから情報を取得するが今回は割愛します。
先ほど作成した[verifyToken]を付与すれば、認証が行われ[req.decoded]にユーザーIDが格納されるので、その値をもとにユーザー情報を取得してレスポンスを返します。
const router = require("express").Router();
const verifyToken = require("../middlewares/verifyToken");
// GET /api/v1/users
router.get("/", verifyToken, (req, res) => {
// 本来はデータベースからユーザーIDを元にデータを取得するが割愛
let results = {};
if (req.decoded.userId == "001") {
results = {
userId: req.decoded.userId,
name: "Tom",
};
} else if (req.decoded.userId == "002") {
results = {
userId: req.decoded.userId,
name: "Mike",
};
}
res.json(results);
});
module.exports = router;
ユーザー情報取得リクエストを試してみる
先ほどログインした時のトークンをリクエストヘッダーに付与して、リクエストする。
GET http://localhost:5000/api/v1/users
Authorization: Bearer xxxxxxxxxxxxxxxxxxxx
認証に成功すれば以下のようにレスポンスがあるはずです。
{
"userId": "00001",
"name": "Tom"
}
まとめ
これで、API側の実装は完了です。本来はデータベースが必須になるのでそのうちMySqlかMongoDBあたりで実装するかもしれません。
とりあえず次回はフロント側の実装をしていきたいと思います。何かまずいところあればコメントお願いします。
参考にしたもの
Introduction to JSON Web Tokens
JWT Authentication Tutorial - Node.js