LoginSignup
11
15

More than 3 years have passed since last update.

React+ReduxアプリにNode.js(express)とJWTで認証・認可周りの処理を実装する①API側

Last updated at Posted at 2020-04-18

はじめに

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を初期化してリターンします。

app.js
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を作成

認証は本来はデータベースから行うが今回は割愛します。

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に設定を記載する。

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]が表示されるのでクリックすれば、リクエストできます。

request.rest
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で設定した値が格納される。

verifyToken.js
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が格納されるので、その値をもとにユーザー情報を取得してレスポンスを返します。

login.js
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;

ユーザー情報取得リクエストを試してみる

先ほどログインした時のトークンをリクエストヘッダーに付与して、リクエストする。

request.rest
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

11
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
15