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

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

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

最終更新日:2018/09/21

前回まででログイン機能ができました。今回の章ではダッシュボードに名前を表示する機能とルートを保護する機能を実装します。
あと少しなので頑張ってください!

ルートを保護する

ダッシュボードは現在http://localhost:3000/dashboardにアクセスすれば誰でもみられる状態になっています。これをログインユーザのみ見られるようにしましょう。

src/components配下にPrivateRoute.jsというファイルを作成し、以下のようにしてください。

import React from 'react';
import { Route, Redirect } from 'react-router-dom';

export const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route {...rest} render={props => (
        localStorage.getItem('token')
            ? <Component {...props} />
            : <Redirect to={{ pathname: '/', state: { from: props.location } }} />
    )} />
)

localStorageにトークンがあれば指定されたコンポーネントを表示し、なければリダイレクトするという処理を持ったルートコンポーネントの拡張です。

次にsrc/App.jsを変更します。

import { PrivateRoute } from './components/PrivateRoute';//追加

// 省略

class App extends Component {
  render() {
    const { classes } = this.props;
    return (
      <div className={classes.root}>
        <PrivateRoute path="/dashboard" component={Dashboard}></PrivateRoute>
        <Route path="/" exact={true} component={Login}></Route>
      </div>
    );
  }
}

これでログインしてない状態でhttp://localhost:3000/dashboardに行くとリダイレクトされるはずです。localstorageをクリアすれば動作確認ができます。

ダッシュボードの名前の表示

それでは、ダッシュボードに名前を表示する機能を実装して行きます。
APIリクエストのヘッダーにトークンを乗せて送るので、src/helpers/auth-header.jsをいうファイルを作って以下のように実装します。

export function authHeader() {
    // return authorization header with jwt token
    let token = JSON.parse(localStorage.getItem('token'));

    if (token) {
        return { 'Authorization': 'Bearer ' + token};
    } else {
        return {};
    }
}

src/helpers/index.jsをつくってexportできるようにします。

export * from './auth-header';

src/components/Dashboard.jsに以下のライフサイクルメソッドを追加しましょう。

componentWillMount() {
   this.props.onMount();
}

action

次にActionCreatorを定義します。

src/actions/user.actions.jsというファイルを作成して以下のようにします。

import { userConstants } from '../constants';
import { userService } from '../services';

export const getMe = () => {
    return dispatch => {
        dispatch(getMeRequest());

        userService.getMe()
            .then(
                data => dispatch(getMeSuccess(data)),
                error => dispatch(getMeFailure(error))
            );
    };
}

const getMeRequest = () => ({
  type: userConstants.GET_ME_REQUEST
});
const getMeSuccess = (data) => ({
  type: userConstants.GET_ME_SUCCESS,
  payload: data
});
const getMeFailure = (error) => ({
  type: userConstants.GET_ME_FAILURE,
  error
});

export const clearMe = () => ({
  type: userConstants.CLEAR_ME
});

src/constants/user.constants.jsは以下のようにします。

export const userConstants = {
  GET_ME_REQUEST: 'GET_ME_REQUEST',
  GET_ME_SUCCESS: 'GET_ME_SUCCESS',
  GET_ME_FAILURE: 'GET_ME_FAILURE',
  CLEAR_ME: 'CLEAR_ME',
}

src/services/user.service.jsに以下を追加します。

import { authHeader } from '../helpers';

export const userService = {
    login,
    logout,
    getMe //追加
};

//省略

function getMe() {
    const requestOptions = {
        method: 'GET',
        headers: authHeader()
    };

    return fetch(`${apiHost}/me`, requestOptions).then(handleResponse);
}

ポイントになるのがローカルストレージにあるトークンをauthHeaderヘルパー関数によってhttpリクエストのヘッダーに付与している点です。

src/constants/index.js

export * from './user.constants';

を追加します。

Reducer

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

const initialState = {
  loading: false,
  me: {}
};

export function user(state = initialState, action) {
  switch (action.type) {
    case userConstants.GET_ME_REQUEST:
      return {
        ...state,
        loading: true,
        error: false,
      };
    case userConstants.GET_ME_SUCCESS:
      return {
        ...state,
        loading: false,
        me: action.payload,
      };
    case userConstants.GET_ME_FAILURE:
      return {
        ...state,
        loading: false,
        error: true,
      };
    case userConstants.CLEAR_ME:
      return {
        ...state,
        me: {},
      }
    default:
      return state
  }
}

src/reducers/index.jsに以下の記述を追加しましょう。

export * from './user.reducer';

Container

src/containers/Dashboard.jsを修正しましょう。

// actionの読み込み
import { logoutAndRedirect } from '../actions/auth.actions';
import { getMe, clearMe } from '../actions/user.actions';

// props経由でAPI経由で取得した自分自身の情報をコンポーネントに渡す
const mapStateToProps = (state, ownProps) => ({
  me: state.user.me
});

const mapDispatchToProps = dispatch => ({
  onMount() {
    dispatch(getMe());
  }
});

props経由でmeがコンポーネントに渡っているのでsrc/components/Dashboard.jsを以下のように書き換えます。

// 省略

render() {
    const { classes, me} = this.props;
    return (
      <div>
        <Header menu="ログアウト" onClick={this.props.logout}/>
        <Paper className={classes.paper} elevation={1}>
          <Typography variant="headline" component="h3">
            <strong>{me.nickname}</strong>さん、ダッシュボードへようこそ!
          </Typography>
          <Typography component="p">
            さあ、JWT認証をマスターしたらSPAアプリケーションを今すぐ開発しましょう!
          </Typography>
        </Paper>
      </div>
    );
  }

これで名前が表示できるようになりました!以上でチュートリアルは終わりです!とても長くなってしまいましたが、最後までお付き合いいただきありがとうございました!

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