【React Router】認証状態・権限に応じてアクセス制御する
- React Routerで特定のページにアクセス制限を設定したい
- ログインしていない場合、ログインページにリダイレクト
- 権限が無い場合、アクセスできない (403ページを表示する)
- 存在しないページの場合、アクセスできない (404ページを表示する)
- 権限は
User
とAdmin
の2種類Admin
のみ/dashboard
にアクセスできる- その他のページは権限問わずアクセスできる
1. 定数を定義
export const PATHS = { HOME: '/', DASHBOARD: '/dashboard', POSTS: '/posts/:postId', SIGNIN: 'signin',} as const satisfies Record<string, `/${string}`>;
export const ROLES = { USER: 'user', ADMIN: 'admin',} as const satisfies Record<string, string>;
export type Role = (typeof ROLES)[keyof typeof ROLES];
2. 権限毎にRouterを実装
import { Route, Routes } from 'react-router-dom';import { PATHS } from '@/constants';
export default function AdminRouter() { return ( <Routes> <Route path={PATHS.HOME} element={<HomePage />} /> <Route path={PATHS.DASHBOARD} element={<DashboardPage />} /> <Route path={PATHS.POSTS} element={<PostsPage />} /> <Route path="*" element={<UnauthorizedPage />} /> </Routes> );}
import { Route, Routes } from 'react-router-dom';import { PATHS } from '@/constants';
export default function UserRouter() { return ( <Routes> <Route path={PATHS.HOME} element={<HomePage />} /> <Route path={PATHS.POSTS} element={<PostsPage />} /> <Route path="*" element={<UnauthorizedPage />} /> </Routes> );}
- 権限毎にRouterを実装
- React Routerの仕様上、同一パスに対して権限毎のアクセス制限ができないため
Admin
のみ’/dashboard’のパスを追加- 想定しないパス (
*
) の時は403ページを表示- これにより
User
は’/dashboard’にアクセスできない - 存在しないページ (404) の場合は後述の
RootRouter.tsx
内で制御する
- これにより
import type { ReactElement } from 'react';import { matchPath, useLocation } from 'react-router-dom';import { AdminRouter } from '@/components/AdminRouter';import { UserRouter } from '@/components/UserRouter';import { PATHS, type Router } from '@/constants';
const routerMap = { admin: <AdminRouter />, user: <UserRouter />,} satisfies Record<Role, ReactElement>;
export default function RootRouter() { const { pathname } = useLocation(); const { isAuthenticated, role } = useAuth(); // 認証状態を返すダミーHook
// 存在するパスかどうかを判定 const existsPath = Object.values(PATHS).some( (path) => !!matchPath(path, pathname), ); if (!existsPath) return <NotFoundPage />; if (!isAuthenticated) return <Navigate to={PATH.SIGNIN} />;
return routerMap;}
- 存在するパスかを
matchPath()
で判定し、否であれば404ページを表示 - ログインしてなければログインページにリダイレクト
- 最終的に、該当する権限のRouterをレンダリング