4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

react+axiosで認証ページ制御を扱う

Posted at

SPA環境におけるページ制御をreact, redux, saga, axiosで実装してみた
※Frontのみ扱います

環境

  • react:16.12
  • react-redux: 7.1.3
  • react-router-dom: 5.1.2

API仕様

  • APIリクエスト時、認証失敗した場合は401を返す

できあがり要件

  • メンバー用ページは認証成功したユーザーのみ表示可能
  • ログイン後に認証が切れた場合、メンバー用ページが表示できないようにする

実装

あらすじ

  • ページに基本となるルーティングを作成する
  • 認証ルート配下はAuthコンポーネントで認証制御
  • 認証状態はredux, redux-sagaを利用して管理
  • requestモジュールから401を検知してreact-routerで遷移

ディレクトリ構成

user
├── api.ts  //api通信用
├── index.ts
├── redux.ts // いわゆるAction,Reducer等
└── saga.ts // Saga

ルーティング

router.tsx
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

const AppRouter: React.FC = () => (
  <Router>
    <Switch>
      <Route exact path='/' component={Top} />
      {/* 認証ルート */}
      <Route path='/member'>
        <Auth>
          <Switch>
            <Route path="/member/signed" component={Signed} />
            <Route component={NotFound} />
          </Switch>
        </Auth>
      </Route>
      <Route component={NotFound} />
    </Switch>
  </Router>
);

Authモジュール

  • 認証状態取得前なら取得取得するまでcircular表示。認証できない場合はサインインページへ遷移
auth.tsx

const Auth = ({ user: { isSignedIn, isFetched }, fetchInfo, children }) => {
  if (!isFetched) {
    fetchInfo();
  }
  return (
    <>
      {isFetched ? (
        <Fetched isSignedIn={isSignedIn} children={children} />
      ) : (
        <Fetching />
      )}
    </>
  );
};

const Fetching = () => (
  <div className={styles.center}>
    <Circular />
  </div>
);
const Fetched = ({ isSignedIn, children }) =>
  isSignedIn ? children : <Redirect to='/sign_in' />;

export default connect(mapStateToProps, mapDispatchToProps)(Auth);

redux, redux-saga関連

  • 認証情報取得。認証通信がエラーの場合はSIGNED_IN = falseをkeepし、authモジュールによってページ遷移させる
redux.ts
const signedIn = () => {
  return {
    type: SIGNED_IN
  };
};
const signedOut = () => {
  return {
    type: SIGNED_OUT
  };
};
const signOut = () => ({ type: SIGN_OUT });
const fetched = () => ({ type: FETCHED });
const fetchInfo = payload => ({ type: FETCH_INFO, payload });
const resetState = () => ({ type: RESET_STATE });
saga.ts
function* fetchInfo() {
  const isSuccess = yield call(api.fetchInfo);

  if (isSuccess) {
    yield all([put({ type: SIGNED_IN }), put({ type: FETCHED })]);
  } else {
    yield all([put({ type: FETCHED }), put({ type: SIGNED_OUT })]);
  }
}
api.ts
export const fetchInfo = async () => {
  let isSignedIn = true;
  const requester = requestManager.get();

  await requester.get(API_PATH.AUTH_INFO).catch(err => {
    isSignedIn = false;
  });

  return isSignedIn;
};

その他通信時に認証が切れた場合のハンドリング

  • axiosをwrapしたrequestモジュールを作成し、認証制御機能を組み込む
utils/request.ts
import axios, { AxiosRequestConfig, AxiosResponse, AxiosInstance } from "axios";
import { mapDispatchToProps as userActions } from "../modules/user";
import { AllState } from "../store";

class RequestManager {
  private store?: AllState;
  private requester?: AxiosInstance;
  setStore(store: AllState) {
    this.store = store;
  }
  getInstance(): AxiosInstance {
    if (!this.store) {
      throw new Error("store is not initialized");
    }
    if (!this.requester) {
      this.requester = this._getBaseInstance();
    }

    return this.requester;
  }
  private _getInstance(): AxiosInstance {
    const instance = axios.create({
      baseURL: "http://localhost:3000",
      headers: { "content-type": "application/json" },
      withCredentials: true
    });

    instance.interceptors.response.use(response => {
      const { status } = response;

      if (status === 401) {
        this.store.dispatch(userActions.resetState());
      }
      return response;
    });

    return instance;
  }
}

export default new RequestManager();


このようにすれば、RequestManagerから生成されたaxiosインスタンス経由でリクエスト時に401の場合、isSignedIn: falseに変更され、認証ルート内の場合はサインインページへ遷移させられる

4
7
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
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?