Защищенные маршруты (Protected Routes) — это механизм ограничения доступа к определенным разделам приложения для неавторизованных пользователей. Рассмотрим современные подходы к реализации.
Создаем компонент-обертку для проверки авторизации:
import { Navigate, Outlet } from 'react-router-dom';
const ProtectedRoute = ({ isAllowed, redirectPath = '/login', children }) => {
if (!isAllowed) {
return <Navigate to={redirectPath} replace />;
}
return children ? children : <Outlet />;
};
Использование:
<Route
path="/dashboard"
element={<ProtectedRoute isAllowed={isAuthenticated} />}
>
<Route index element={<Dashboard />} />
<Route path="settings" element={<Settings />} />
</Route>
Для систем с ролевой моделью:
const RoleProtectedRoute = ({ roles, userRole, children }) => {
const isAllowed = roles.includes(userRole);
if (!isAllowed) {
return <Navigate to="/not-authorized" replace />;
}
return children ? children : <Outlet />;
};
Использование:
<Route
path="/admin"
element={
<ProtectedRoute isAllowed={isAuthenticated}>
<RoleProtectedRoute roles={['admin', 'superadmin']} userRole={currentUser.role} />
</ProtectedRoute>
}
>
<Route index element={<AdminPanel />} />
</Route>
Лучше всего работает с глобальным состоянием:
const AuthProtectedRoute = () => {
const { isAuthenticated, isLoading } = useAuth(); // Ваш хук/контекст
if (isLoading) {
return <LoadingSpinner />;
}
return isAuthenticated ? <Outlet /> : <Navigate to="/login" replace />;
};
Чтобы запомнить, откуда пришел пользователь:
const ProtectedRouteWithHistory = () => {
const location = useLocation();
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return <Outlet />;
};
На странице логина:
const location = useLocation();
const navigate = useNavigate();
const handleLogin = () => {
login().then(() => {
navigate(location.state?.from || '/', { replace: true });
});
};
Полноценная защита маршрутов с проверкой токена:
const TokenProtectedRoute = () => {
const [isValid, setIsValid] = useState(null);
const { checkAuth } = useAuth();
useEffect(() => {
const verifyToken = async () => {
try {
await checkAuth(); // Проверка access/refresh токенов
setIsValid(true);
} catch {
setIsValid(false);
}
};
verifyToken();
}, []);
if (isValid === null) return <LoadingPage />;
return isValid ? <Outlet /> : <Navigate to="/login" replace />;
};
Чтобы авторизованные пользователи не попадали на страницы логина/регистрации:
const PublicRoute = ({ children }) => {
const { isAuthenticated } = useAuth();
return isAuthenticated ? <Navigate to="/" replace /> : children;
};
Использование:
<Route path="/login" element={<PublicRoute><LoginPage /></PublicRoute>} />
Основные подходы:
ProtectedRoute
)RoleProtectedRoute
)Ключевые элементы:
Navigate
для перенаправленияOutlet
для вложенных маршрутовРекомендации:
replace
при редиректахПример полной реализации:
<Route element={<TokenProtectedRoute />}>
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Route>
<Route element={<RoleProtectedRoute roles={['admin']} />}>
<Route path="/admin" element={<AdminPanel />} />
</Route>
<Route path="/login" element={<PublicRoute><Login /></PublicRoute>} />