現役フリーランスエンジニアが運営するテックメディア。日々の業務で得た知識を発信していきます!

  1. フロントエンド
  2. 4224 view

React+ReduxアプリケーションにJWT認証を導入する完全ガイド【ソースコードあり】

最終更新日:2018/09/21

JWT認証の導入

前のページではルーティングとreduxの設定を行いました。ようやくJWT認証の導入です!APIへのリクエストは非同期処理ですので非同期処理をするライブラリとしてredux-thunkを使います。

非同期処理を実装する

早速redux-thunkをインストールします。

npm install --save redux-thunk

src/index.jsを修正しましょう。thunkを読み込んでReduxMiddlewareにredux-thunkを追加する必要があります。

import { createStore, combineReducers, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';

import * as reducers from './reducers';

// ルーターの設定
import createBrowserHistory from 'history/createBrowserHistory';
import { routerReducer, routerMiddleware, ConnectedRouter } from 'react-router-redux';

// 追加
import thunk from 'redux-thunk';

import logger from 'redux-logger';

// historyインスタンスを作成する処理追加
const history = createBrowserHistory();

// Storeを作成する
const store = createStore(
  combineReducers({
    ...reducers,
    router: routerReducer
  }),
  // applyMiddleware関数でredux-loggerを設定
  applyMiddleware(
    routerMiddleware(history),
    thunk, //追加
    logger
  )
);

これで非同期処理の設定は完了です。それでは、APIリクエストの準備を整えて行きましょう。
LoginコンポーネントからActionを呼びたすところを実装します。

stc/components/Login.jsを開いてhandleSubmit関数を以下のように修正しましょう。

handleSubmit(e) {
    e.preventDefault();
    const { username, password } = this.state;
    // 追加
    if (username && password) {
        this.props.login(username,password);
    }
  }

これによってhandleSubmitが実行された時(ログインボタンが押された時)props経由で渡ってきたloginアクションを実行することができます。

src/actions/auth.actions.jsActionCreatorを定義します。loginやlogoutなどに必要なactionを一通り定義します。

import { authConstants } from '../constants';
import { userService } from '../services';
import { push } from 'react-router-redux';

export const login = (username, password) => {
    return dispatch => {
        dispatch(loginRequest({ username }));

        userService.login(username, password)
          .then(
              data => {
                  dispatch(loginSuccess(data));
                  dispatch(push('/dashboard'));
              },
              error => {
                  dispatch(loginFailure(error));
              }
          );
    };

}

export const logoutAndRedirect = () => {
  return dispatch => {
    userService.logout();
    dispatch(logout());
    dispatch(push('/'));
  }
}

const logout = () => ({
  type: authConstants.LOGOUT
});


const loginRequest = data => ({
  type: authConstants.LOGIN_REQUEST,
});


const loginSuccess = data => ({
  type: authConstants.LOGIN_SUCCESS,
  payload: data
});
const loginFailure = error => ({
  type: authConstants.LOGIN_FAILURE,
  payload: error
});

この中でloginはredux-thunkを用いた非同期処理を行なっています。またこれらが依存するauth.constants.js,user.service.jsは以下のようになります。

src/constants/auth.constants.js
このファイルではaction名を定数化しています。

export const authConstants = {
    LOGIN_REQUEST: 'LOGIN_REQUEST',
    LOGIN_SUCCESS: 'LOGIN_SUCCESS',
    LOGIN_FAILURE: 'LOGIN_FAILURE',

    LOGOUT: 'LOGOUT',
};

src/services/user.service.js
このファイルではloginやlogout時に必要な具体的な処理をserviceとして提供します。


const apiHost = 'http://localhost:8080/api'; export const userService = { login, logout, getMe }; function login(username, password) { const requestOptions = { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify( { name: username, password } ) }; return fetch(`${apiHost}/authenticate`, requestOptions) .then(handleResponse) .then(data => { // login successful if there's a jwt token in the response if (data.token) { // store user details and jwt token in local storage to keep user logged in between page refreshes localStorage.setItem('token', JSON.stringify(data.token)); } return data; }); } function logout() { // ログアウト時にはローカルストレージからuserアイテムを削除する localStorage.removeItem('token'); } function handleResponse(response) { return response.text().then(text => { const data = text && JSON.parse(text); if (!response.ok) { if (response.status === 401) { // auto logout if 401 response returned from api logout(); } const error = (data && data.message) || response.statusText; return Promise.reject(error); } return data; }); }

これらがexportできるようにsrc/services/index.js,src/constants/index.jsを作成してそれぞれ以下のようにしてください。

src/services/index.js

export * from './user.service';

src/constants/index.js

export * from './auth.constants';

Reducerは以下のような感じです。

src/reducers/auth.reducer.js

import { authConstants } from '../constants';

let token = JSON.parse(localStorage.getItem('token'));
const initialState = token ?
{
  loggingIn: false,
  loggedIn: true,
  token: token,
  error: false,
}
: {
  loggingIn: false,
  loggedIn: false,
  token: null,
  error: false,
};

export function auth(state = initialState, action) {
  switch (action.type) {
    case authConstants.LOGIN_REQUEST:
      return {
        ...state,
        loggingIn: true,
        loggedIn: false,
        error: false,
      };
    case authConstants.LOGIN_SUCCESS:
      return {
        ...state,
        loggingIn: false,
        loggedIn: true,
        token: action.payload.token,
      };
    case authConstants.LOGIN_FAILURE:
      return {
        ...state,
        error: true,
      };
    case authConstants.LOGOUT:
      return {
        ...state,
        loggedIn: false,
        token: null,
        error: false
      };
    default:
      return state
  }
}

src/reducers/index.jsというファイルを作成して、

export * from './auth.reducer';

という記述を追加しましょう。

src/containers/Login.jsmapDispatchToProps関数を修正してLoginコンポーネントにprops経由でloginを渡します。

// 追加
import { login } from '../actions/auth.actions';

// 省略

const mapDispatchToProps = dispatch => ({
  login(username, password) {
    dispatch(login(username, password));
  }
});

以上で完了です。JWTモックサーバを立ち上げてreactプロジェクトも立ち上げてみましょう!

npm start

このように動作しましたでしょうか?ブラウザのコンソールに発行されたactionやstateが表示されているのがわかります。動かない場合はブラウザのコンソール等のデバッグ機能を確認してみましょう!

以上でJWT認証の導入は終了です。次のページではダッシュボードに名前を表示する機能とルート保護の機能をつけます。

The following two tabs change content below.
riri

riri

WINDII(ウィンディ)は、フリーランスエンジニアが運営するテックメディアです。 日々の業務で得た知見を、皆さんに役立つコンテンツにして発信していくので応援よろしくお願いします! また、Slackで無料コミュニティも運営しています。たくさんのエンジニアが参加していて、プログラミングの相談や雑談などをしている楽しいコミュニティなので、興味ある方はぜひお気軽にご参加ください。 Slackコミュニティはこちらから
1

2

3

フロントエンドの最近記事

  1. IOSアプリをAppStoreに公開する手順書(Ionic)

  2. IonicAcademyでIonic&Angularでのアプリ開発を学ぶ

  3. Ionic4+firebaseで認証をする方法【ログイン、ログアウト機能の実装】

  4. ハイブリッドアプリ開発チュートリアル【Ionic4+OnsenUI】

  5. Ionic4+Angular Materialの始め方【Ionic事前調査兵団】

関連記事

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

PAGE TOP