プログラム関係の備忘録。技術系記事1000記事以上を目指すブログ

【解説編】node.js expressを使ったAPIサーバーでJson Web Tokenの使い方

  • 2019年2月3日
  • 2019年2月3日
  • JavaScript
  • 382view
  • 1件

はじめに

書いている内容

Reactとexpressを使ったWebサービスで、ユーザー情報の認証機能をJson Web Tokenを用いて実装する方法。

MVCモデルを用いたLaravelなどでは初めから用意されているAuthを使ったりして簡単に実装できますが、React単体でそういう機能はありません。
expressだけでならPassportというのも使えるみたいだけど、ちょっとやりたいことと違う。

そこで調べてみると、Json Web Tokenを使って認証をするというのが今回やりたいことに近かった。
しかし、いまいち使い方がわからず苦労したので、今回できた結果を細かく残しておく。

JsonWebTokenを使ってなにをする?

流れはこんな感じになります。

①ログイン用のAPIをReactから叩く。パラメータはアカウント名とパスワード。
②リクエストを受けたAPIサーバーは、パラメータを元にDBを参照。
③参照結果OKの場合、ユーザーアカウント、鍵、利用期限を暗号化したトークンを返す。
②フロント側はsessionstorageにAPIからもらったトークンを保存
③認証が必要なAPIを叩くときは、トークンをリクエストヘッダーに付与して叩く
④API側はトークンの有無、利用期限などをチェックしてから、処理を受け付ける

※つっこみどころはお手数ですがコメントにて教えてください。

構成

node.jsでcreate react appを使いお手軽に用意したReactをフロントエンド。
APIサーバーとして同じくnode.jsを利用、expressを用いて実装していく。

この記事では使いませんがDBはMySQLです。

準備

インストール

①node.jsの準備
まず大前提として、node.jsのインストールと、
パッケージにexpress-generator、jsonwebtokenを入れた環境を作っておく。

express-generatorを使うとあらかじめディレクトリ構成やメインファイルができたものができあがりますが、最小構成で作るならexpress-generatorを使う必要はなく通常のexpressで問題ありません。

自分はDockerを用いて環境を作っているので、こんな感じのDockerfileになりました。

ENV NODE_ENV=development
RUN apt update && apt install -y vim && \
npm i -g express-generator && \
express --view=pug api && \
npm i mysql --save && \
npm i cors --save && \
npm i jsonwebtoken
RUN chmod 666 /api
WORKDIR /api
RUN npm install
CMD ["npm", "start"]

※npm installの部分はわかりやすいよう別々に書いています。
※corsは別ポートにあるReact側からリクエストを受け付けるために利用します。

②POSTMANのインストール
APIのリクエストをGUIで実行できるソフトです。
コマンドでたたいて確認するのもいいですが、便利なので使います。
DLはこちら
https://www.getpostman.com/

expressでJsonWebTokenを利用するための準備

できあがったexpressのApp.jsに認証機能を追加するための準備をしていきます。

app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var jwt = require( 'jsonwebtoken' );
var cors = require( 'cors' );
var app = express();

app.use( cors() );

app.get('/', (req,res) => {
  res.json({
    message: 'GETのお試しです'
  })
})

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

まずルートディレクトリにアクセスするとメッセージを返す処理を追加
これは認証なしでAPIの動作を確認するためのサンプルです。

この状態でサーバーを起動、POSTMANを使い、ルートディレクトリにGETでアクセスすると、JSON形式で「GETのお試しです」と返ってきます。

次に、同じようにPOSTの場合のサンプルをapp.jsに追加します。

app.post('/post', (req,res) => {
  res.json({
    message: 'POSTのお試しです'
  })
})

POSTMANを使い、今度は/postにPOSTアクセスすることで、「POSTのお試しです」と返ってくるのが確認できれば、準備OKです。

JsonWebTokenの機能を実装していく

前項で準備が整ったら、ようやくトークンを利用した処理を追加していきます。
まずはトークンを発行する処理、続いてトークンを認証する処理を書いていきます。

トークンを発行する

ログイン用APIを叩いてトークンを発行する処理

app.js

app.post('/login', (req,res) => {

  // お試しユーザー
  const user = {
    id: 1,
    username: 'テストユーザー'
  }

  jwt.sign({user: user}, 'secretkey', {expiresIn: '1h'}, (err, token) => {
    res.json({
      token
    })
  })

})

この処理では、/loginにアクセスした際に、あらかじめ用意したユーザー情報(const user)を暗号化し、トークンとして返す処理をしています。
{expiresIn: ‘1h’}では有効期限を指定していて、この例では1時間有効なトークンが発行されます。
そして結果をjson形式で返却します。

※実際にはMySQLに接続し、ユーザー情報の有無を参照しますが、まずは認証部分に焦点をあててやっていきます。

これでPOSTMANで/loginにアクセスすると、tokenに長い文字列が付与されたjsonが返ってきます。

トークンを認証する

router.post('/post', verifyToken, (req, res) => {
  jwt.verify(req.token, 'secretkey', (err, authData) => {
    if(err){
      res.sendStatus(403)
    } else {
    res.json({
        authData
      })
    }
  })
})

function verifyToken(req, res, next) {
    // リクエストヘッダーから認証値を取得
    const bearerHeader = req.headers['authorization']
    if(typeof bearerHeader !== 'undefined'){
        // Bearerの後ろのスペース以降がトークンになる為splitして取得
        const bearer = bearerHeader.split(' ')
        // トークンを保持して次の処理に進む
        const bearerToken = bearer[1]
        req.token = bearerToken
        next()
    } else {
        // トークンが存在しない場合にはエラー
        res.sendStatus(403)
    }
}

/postにアクセスした際に、varifyTokenという関数を実行します。
varifyTokenでは、リクエストヘッダーからトークンの有無をチェックしています。
トークンが存在する場合には次の処理に進みます。

jwt.verifyでトークンを復号し、authDataに格納します。
結果をjson形式で返却します。

POSTMANで/loginにアクセスで発行されたトークンをコピーし
/postのヘッダー(authorization)にトークンをペーストし、アクセスすると、
user = {
id: 1,
username: ‘テストユーザー’
}
が返ってきます。

リクエストヘッダーにトークンを付与せずにアクセスした場合は、403が返ってくるのが確認できます。

以上で、トークンの発行、トークンを利用した認証が完成しました。

さいごに

わかりやすいように全ての処理をapp.jsに書いていますが、使いまわす機能や、シークレットキーの設定などは別モジュールに分けてあげたほうがいいですね。

この記事で足りていないことは、

①実際にReact側からAPIを叩く部分
②ユーザーデータをDBから取ってくる部分
③実務向けにモジュール分けする部分

といった感じになります。

長くなってしまったので、上記に関しては別記事で書いてみます。

続き
【実践編】Reactとexpress。Json Web Tokenで認証機能を実装

また、この記事は以下の動画を参考に作成しました。
大変わかりやすいので、この記事でうまくいかなかった場合にはぜひ見てみてください。
解説は英語ですが、画面を見ながら確認できるので学習コストはかかりません。

https://www.youtube.com/watch?v=7nafaH9SddU