Редиректы для неавторизованных пользователей

TanStack Router предоставляет гибкий способ управления навигацией в приложениях на JavaScript, включая сценарии, когда необходимо ограничивать доступ к определённым маршрутам. Одной из ключевых задач является организация редиректов для неавторизованных пользователей. Рассмотрим этот процесс детально.


Определение маршрутов с проверкой авторизации

Маршруты в TanStack Router определяются с помощью объектов Route. Для контроля доступа можно использовать функцию loader или свойство onLoad, чтобы проверять состояние авторизации перед рендерингом страницы. Пример структуры маршрута с проверкой:

import { createRouter, Route } from '@tanstack/router';

const isAuthenticated = () => {
  // Здесь должна быть логика проверки токена или сессии
  return Boolean(localStorage.getItem('authToken'));
};

const protectedRoute = new Route({
  path: '/dashboard',
  element: <DashboardPage />,
  loader: async () => {
    if (!isAuthenticated()) {
      throw new Error('NOT_AUTHORIZED');
    }
    return null;
  }
});

const loginRoute = new Route({
  path: '/login',
  element: <LoginPage />
});

const router = createRouter({
  routeTree: protectedRoute.addRoute(loginRoute)
});

Ключевые моменты:

  • loader выполняется перед рендерингом страницы.
  • В случае неавторизованного пользователя можно выбросить ошибку или вернуть специальный объект.
  • Стратегия редиректа строится на обработке этой ошибки.

Использование глобального перехватчика ошибок для редиректа

В TanStack Router можно настроить глобальный обработчик ошибок через onRouteError. Он позволяет централизованно перенаправлять пользователей при возникновении ошибок авторизации:

router.onRouteEr ror = (error, route) => {
  if (error.message === 'NOT_AUTHORIZED') {
    router.navigate('/login');
  }
};

Принципы работы:

  • Любая ошибка в loader маршрута перехватывается глобальным обработчиком.
  • Неавторизованные пользователи перенаправляются на страницу логина.
  • При этом можно сохранять исходный маршрут для последующего возврата после авторизации.

Перенаправление с сохранением исходного URL

Для улучшения UX часто требуется возвращать пользователя на страницу, которую он изначально хотел открыть. Это можно реализовать с помощью query-параметра redirectTo:

const protectedRoute = new Route({
  path: '/dashboard',
  element: <DashboardPage />,
  loader: async () => {
    if (!isAuthenticated()) {
      router.navigate(`/login?redirectTo=${encodeURIComponent(window.location.pathname)}`);
      return null;
    }
    return null;
  }
});

На странице логина после успешной авторизации можно выполнить редирект на исходный маршрут:

const handleLogin = () => {
  performLogin(); // Логика авторизации
  const params = new URLSearchParams(window.location.search);
  const redirectTo = params.get('redirectTo') || '/dashboard';
  router.navigate(redirectTo);
};

Важные моменты:

  • encodeURIComponent защищает URL от некорректного форматирования.
  • По завершении логина осуществляется навигация к изначально запрошенному маршруту.
  • Поддерживается цепочка редиректов без потери состояния.

Комбинирование с компонентами защиты маршрутов

В TanStack Router можно использовать wrappers или компоненты-обёртки для защиты маршрутов. Это особенно удобно при сложных приложениях с множеством защищённых страниц:

const AuthWrapper = ({ children }) => {
  if (!isAuthenticated()) {
    router.navigate(`/login?redirectTo=${encodeURIComponent(window.location.pathname)}`);
    return null;
  }
  return children;
};

const protectedRoute = new Route({
  path: '/dashboard',
  element: <AuthWrapper><DashboardPage /></AuthWrapper>
});

Преимущества подхода:

  • Сохраняется декларативность маршрутов.
  • Возможна многократная переиспользуемость компонента защиты.
  • Легко комбинировать с различными ролями пользователей и условиями доступа.

Поддержка асинхронной проверки авторизации

Если проверка авторизации требует асинхронного запроса к серверу (например, получение сессии пользователя), loader можно сделать асинхронным:

const protectedRoute = new Route({
  path: '/dashboard',
  element: <DashboardPage />,
  loader: async () => {
    const user = await fetchCurrentUser();
    if (!user) {
      router.navigate(`/login?redirectTo=${encodeURIComponent(window.location.pathname)}`);
      return null;
    }
    return user;
  }
});

Особенности асинхронного подхода:

  • Пользователь видит страницу только после проверки.
  • Можно использовать спиннер или skeleton-рендеринг до завершения запроса.
  • Исключается возможность случайного доступа к защищённым компонентам до завершения проверки.

Рекомендации по организации редиректов

  1. Централизованная проверка: использовать глобальные обработчики ошибок или обёртки, чтобы не дублировать логику на каждом маршруте.
  2. Сохранение исходного пути: всегда передавать redirectTo, чтобы пользователь возвращался на нужную страницу.
  3. Асинхронная авторизация: учитывать задержку при загрузке данных пользователя, показывать индикаторы загрузки.
  4. Чёткое управление состоянием: хранить информацию о текущей сессии в глобальном хранилище (Redux, Zustand или React Context) для удобного доступа в loader и обёртках.
  5. Локальные и глобальные редиректы: комбинировать loader, обёртки и onRouteError для комплексного контроля доступа.

Этот подход обеспечивает надёжную защиту маршрутов в приложениях на TanStack Router и позволяет создавать гибкую систему редиректов для неавторизованных пользователей, сохраняя удобство навигации и минимизируя дублирование кода.