Обработка истекших сессий

В современных веб-приложениях управление сессиями пользователей является критически важным аспектом безопасности и пользовательского опыта. React Router предоставляет гибкие инструменты для перенаправления пользователей и контроля доступа к различным маршрутам, что особенно актуально при работе с истекшими сессиями.


Перехват и проверка сессий

Для обработки истекших сессий используется концепция защищённых маршрутов. Создаётся компонент-обёртка, который проверяет состояние аутентификации пользователя перед отображением целевого компонента.

Пример защищённого маршрута:

import { Navigate } from 'react-router-dom';

function ProtectedRoute({ isAuthenticated, children }) {
  if (!isAuthenticated) {
    return ;
  }
  return children;
}

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

  • isAuthenticated — логическое состояние аутентификации, которое можно получать из контекста, Redux или хранилища состояния.
  • Navigate используется для программного перенаправления без перезагрузки страницы.
  • replace предотвращает сохранение текущего URL в истории браузера, что исключает возможность возвращения на защищённую страницу после выхода.

Хранение информации о сессии

Для корректной работы защищённых маршрутов необходимо отслеживать состояние сессии. Наиболее распространённые подходы:

  1. JWT в локальном хранилище (localStorage/sessionStorage) При получении токена от сервера его сохраняют в localStorage. Проверка актуальности выполняется перед рендером маршрута:

    const token = localStorage.getItem('token');
    const isAuthenticated = token !== null && !isTokenExpired(token);

    Функция isTokenExpired проверяет дату истечения срока действия токена.

  2. Контекст состояния (React Context) Более реактивный метод, который позволяет компонентам подписываться на изменения состояния аутентификации:

    import { createContext, useContext, useState } from 'react';
    
    const AuthContext = createContext();
    
    export function AuthProvider({ children }) {
      const [isAuthenticated, setIsAuthenticated] = useState(false);
    
      return (
        
          {children}
        
      );
    }
    
    export function useAuth() {
      return useContext(AuthContext);
    }

    Это позволяет обновлять состояние сессии в режиме реального времени и автоматически перенаправлять пользователя на страницу входа при истечении токена.


Перенаправление при истечении сессии

React Router 6 предоставляет компонент , который используется для управления переходами. Для динамического контроля состояния сессии рекомендуется использовать эффект React:

import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

function SessionChecker({ isAuthenticated }) {
  const navigate = useNavigate();

  useEffect(() => {
    if (!isAuthenticated) {
      navigate('/login', { replace: true });
    }
  }, [isAuthenticated, navigate]);

  return null;
}

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

  • Можно вызывать редирект в любом компоненте, который связан с жизненным циклом сессии.
  • Обеспечивается автоматическая реакция на изменения состояния аутентификации.

Обработка истёкших сессий при запросах к серверу

Даже при наличии защищённых маршрутов, пользователь может оставаться на странице до истечения сессии. Для обработки истёкшей сессии на уровне API-запросов рекомендуется:

  1. Проверять код ответа сервера (например, 401 Unauthorized).
  2. Выполнять программное перенаправление при обнаружении ошибки:
async function fetchWithAuth(url, options = {}) {
  const token = localStorage.getItem('token');

  const response = await fetch(url, {
    ...options,
    headers: {
      'Authorization': `Bearer ${token}`,
      ...options.headers
    }
  });

  if (response.status === 401) {
    localStorage.removeItem('token');
    window.location.href = '/login';
    return;
  }

  return response.json();
}

Такой подход обеспечивает согласованность между состоянием сессии на клиенте и сервере.


Редиректы с сохранением исходного URL

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

import { Navigate, useLocation } from 'react-router-dom';

function ProtectedRoute({ isAuthenticated, children }) {
  const location = useLocation();

  if (!isAuthenticated) {
    return ;
  }

  return children;
}

После успешного входа можно прочитать state.from и перенаправить пользователя обратно:

import { useLocation, useNavigate } from 'react-router-dom';

function Login() {
  const location = useLocation();
  const navigate = useNavigate();
  const from = location.state?.from?.pathname || '/';

  function handleLogin() {
    // логика аутентификации
    navigate(from, { replace: true });
  }
}

Отслеживание активности пользователя

Для автоматического выхода при бездействии можно использовать таймеры на клиенте, который по истечении определённого периода неактивности сбрасывает токен и выполняет редирект.

Пример реализации с хуком useEffect:

import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

function useAutoLogout(timeout = 15 * 60 * 1000) {
  const navigate = useNavigate();

  useEffect(() => {
    let timer = setTimeout(() => {
      localStorage.removeItem('token');
      navigate('/login', { replace: true });
    }, timeout);

    const resetTimer = () => {
      clearTimeout(timer);
      timer = setTimeout(() => {
        localStorage.removeItem('token');
        navigate('/login', { replace: true });
      }, timeout);
    };

    window.addEventListener('mousemove', resetTimer);
    window.addEventListener('keydown', resetTimer);

    return () => {
      clearTimeout(timer);
      window.removeEventListener('mousemove', resetTimer);
      window.removeEventListener('keydown', resetTimer);
    };
  }, [navigate, timeout]);
}

Такой подход обеспечивает комплексное управление истёкшими сессиями и предотвращает доступ к защищённым ресурсам после длительного бездействия.


Вывод

В React Router обработка истёкших сессий строится на комбинации защищённых маршрутов, программных редиректов и контроля состояния аутентификации. Использование контекста, проверок на уровне API и сохранения исходного URL позволяет реализовать безопасное и удобное для пользователя управление сессиями.