0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React.ContextとCookieによる簡易的な認証制御

Posted at

React.Context とは?

propsで階層を順に下って値を引き渡すのではなく、下位階層すべてにプロパティを引き渡しちゃおうというもの。

コンテクストは各階層で手動でプロパティを下に渡すことなく、コンポーネントツリー内でデータを渡す方法を提供します。
https://ja.legacy.reactjs.org/docs/context.html

使い方

想定ケース

  • 特定のページをログイン済みユーザーにだけ表示する。
  • ログインしていない場合はログインページにリダイレクトさせる。
  • ログイン成功したらCookieに情報を残しログイン済みとして扱う。

認証管理用コンポーネント

ログイン機能、ログアウト機能を提供。ログインしたらCookieに情報を残す。

AuthContext.jsx
import { setCookie } from '../utils/CookieUtil';
import React, { useContext} from 'react';

// 認証情報を共有するためのコンテキストを作成
const AuthContext = React.createContext();

// 認証コンテキストを簡単に利用するためのカスタムフック
export function useAuth() {
  return useContext(AuthContext);
}

// 認証情報を提供するプロバイダーコンポーネント
export default function AuthProvider({ children }) {

  // ユーザーログイン機能
  async function login(id, password) {
    let status = "200";
    let message = "OK";
    // ログイン処理(仮にtest/testで認証OKとする)
    if (id === 'test' && password === 'test') {
      // ログイン成功時の処理
      // クッキーに認証情報を保存
      setCookie('isAuth', 'true', 30);
    } else {
      status = "401";
      message = "Unauthorized";
    }
    return { status, message };
  }

  // ユーザーログアウト機能
  function logout() {
    deleteCookie('isAuth');
    return { status: "200", message: "OK" };
  }

  // コンテキストで提供する値
  const value = {
    login,       // ログイン機能
    logout       // ログアウト機能
  };

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}

ルーター

先ほど作成したAuthContextを設置し、下位階層がログイン管理機能を使用できるように設定。
また、認証が必要なページ(/uop)へのアクセス、かつ、Cookieに情報がない場合は、ログインページ(/login)にリダイレクトする。

App.jsx
import { useEffect } from 'react';
import './App.css';
import { BrowserRouter, Route, Routes, useNavigate, useLocation } from 'react-router-dom';
import AuthContext from './contexts/AuthContext.jsx';
import UserOnlyPage from './components/UserOnlyPage.jsx';
import FreePage from './components/FreePage.jsx';
import Loginform from './components/Loginform.jsx';
import { getCookie } from './utils/CookieUtil';

function App() {
  return (
    <AuthContext>
      <BrowserRouter>
        <AuthHandler />
      </BrowserRouter>
    </AuthContext>
  );
}

function AuthHandler() {
  const navigate = useNavigate();
  const location = useLocation();

  useEffect(() => {
    const isAuth = getCookie('isAuth');

    // 認証が必要なルートをチェック
    if (location.pathname === '/uop' && isAuth !== 'true') {
      navigate('/login'); // 認証が必要な場合はログインページにリダイレクト
    }
  }, [navigate, location]);

  return (
    <Routes>
      <Route path='/uop' element={<UserOnlyPage />} />
      <Route path='/free' element={<FreePage />} />
      <Route path='/login' element={<Loginform />} />
    </Routes>
  );
}

export default App;

ログインフォーム

未認証の場合のリダイレクト先。
ログインに成功したらCookieに情報をセットされ、以降は認証済みとして扱われるようになる。

LoginForm.jsx
//ログインフォーム
import {React, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
// 認証関連のカスタムフックをインポート
import { useAuth } from '../contexts/AuthContext';
import {deleteCookie} from '../utils/CookieUtil';

export default function Loginform () {
  const [id1, setId1] = useState('');
  const [id2, setId2] = useState('');
  const [password, setPassword] = useState('');
  // 認証関連のカスタムフックを利用
  const { login } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    // ログインページに遷移した際にクッキーを削除
    deleteCookie('isAuth');
  }, []);

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      // ログイン処理
      const res = await login(id1 + id2, password); 
      if (res.status === "200") {
        navigate('/');
      } else {
        alert('ログインに失敗しました');
      }
    } catch (error) {
      console.error('login error', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor='id1'>氏名:</label>
      <input
        type='text'
        id='id1'
        value={id1}
        onChange={(e) => setId1(e.target.value)}
      />
      <input
        type='text'
        id='id2'
        value={id2}
        onChange={(e) => setId2(e.target.value)}
      />
      <label htmlFor='password'>Password:</label>
      <input
        type='password'
        id='password'
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type='submit'>Login</button>
    </form>
  );
}

その他:Cookie管理用util

Cookie作成、削除など機能提供用。
react-cookieを使ったほうがいいけど勉強のために自作。

CookieUtil.jsx
// クッキー設定関数
export const setCookie = (name, value, minutes) => {
  const d = new Date();
  d.setTime(d.getTime() + (minutes * 60 * 1000)); // 分単位で有効期限を設定
  const expires = "expires=" + d.toUTCString();
  document.cookie = `${name}=${value}; ${expires}; path=/`;
};

// クッキー取得関数
export const getCookie = (name) => {
  const cname = name + "=";
  const decodedCookie = decodeURIComponent(document.cookie);
  const ca = decodedCookie.split(';');
  for (let i = 0; i < ca.length; i++) {
    let c = ca[i];
    while (c.charAt(0) === ' ') c = c.substring(1);
    if (c.indexOf(cname) === 0) return c.substring(cname.length, c.length);
  }
  return "";
};

// クッキー削除関数
export const deleteCookie = (name) => {
  document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
};

参考:フォルダ階層

src
┗ components
 ┗ FreePage.jsx
  ┗ Loginform.jsx
 ┗ UserOnlyPage.jsx';
┗ contexts
 ┗ AuthContext.jsx
┗ utils
 ┗ CookieUtil.jsx
App.jsx

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?