← Назад к вопросам

Как понимаешь в какой момент истек токен?

1.3 Junior🔥 141 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Определение истечения токена на фронтенде

Это критически важный аспект аутентификации. На фронтенде есть несколько способов проверить, истек ли токен, и каждый имеет свои преимущества и ограничения.

1. Проверка через JWT decoding

JWT токены содержат payload с информацией об истечении. Ты можешь декодировать токен и проверить exp (expiration) claim:

import jwt_decode from 'jwt-decode';

function isTokenExpired(token) {
  try {
    const decoded = jwt_decode(token);
    const currentTime = Date.now() / 1000; // в секундах
    
    // exp - это Unix timestamp когда истекает токен
    return decoded.exp < currentTime;
  } catch (error) {
    return true; // Если ошибка декодирования, считаем токен невалидным
  }
}

const token = localStorage.getItem('access_token');
if (isTokenExpired(token)) {
  console.log('Токен истек');
  // Попробовать обновить токен или перенаправить на логин
}

2. Проверка на основе HTTP 401 ответа

Самый надежный способ - это когда сервер возвращает 401 Unauthorized:

const api = axios.create({
  baseURL: 'https://api.example.com'
});

// Перехватчик ответов
api.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401) {
      console.log('Токен истек (401 от сервера)');
      
      // Попытка обновить токен
      try {
        const refreshToken = localStorage.getItem('refresh_token');
        const response = await axios.post(
          'https://api.example.com/auth/refresh',
          { refresh_token: refreshToken }
        );
        
        const newToken = response.data.access_token;
        localStorage.setItem('access_token', newToken);
        
        // Повторить исходный запрос с новым токеном
        error.config.headers.Authorization = `Bearer ${newToken}`;
        return api(error.config);
      } catch (refreshError) {
        // Refresh токен тоже истек, отправляем на логин
        window.location.href = '/login';
        return Promise.reject(refreshError);
      }
    }
    
    return Promise.reject(error);
  }
);

3. Предзагрузка нового токена (Refresh Token)

Это лучшая практика - обновлять токен ДО его истечения:

class TokenManager {
  constructor() {
    this.accessToken = localStorage.getItem('access_token');
    this.refreshToken = localStorage.getItem('refresh_token');
    this.refreshThreshold = 60 * 1000; // Обновить за минуту до истечения
  }

  isExpiringSoon() {
    try {
      const decoded = jwt_decode(this.accessToken);
      const currentTime = Date.now() / 1000;
      const expiresIn = (decoded.exp - currentTime) * 1000; // в миллисекундах
      
      return expiresIn < this.refreshThreshold;
    } catch (error) {
      return true;
    }
  }

  async refreshAccessToken() {
    try {
      const response = await axios.post('/api/v1/auth/refresh', {
        refresh_token: this.refreshToken
      });
      
      const newToken = response.data.access_token;
      localStorage.setItem('access_token', newToken);
      this.accessToken = newToken;
      
      return newToken;
    } catch (error) {
      console.error('Ошибка при обновлении токена');
      throw error;
    }
  }

  setupAutoRefresh() {
    setInterval(() => {
      if (this.isExpiringSoon()) {
        this.refreshAccessToken().catch(() => {
          // Перенаправить на логин если не удалось обновить
          window.location.href = '/login';
        });
      }
    }, 30 * 1000); // Проверять каждые 30 секунд
  }
}

const tokenManager = new TokenManager();
tokenManager.setupAutoRefresh();

4. React хук для управления токеном

import { useEffect, useState } from 'react';
import jwt_decode from 'jwt-decode';

function useTokenExpiration() {
  const [isExpired, setIsExpired] = useState(false);
  const [expiresIn, setExpiresIn] = useState(null);

  useEffect(() => {
    const token = localStorage.getItem('access_token');
    if (!token) return;

    const checkExpiration = () => {
      try {
        const decoded = jwt_decode(token);
        const currentTime = Date.now() / 1000;
        const timeLeft = decoded.exp - currentTime;
        
        setExpiresIn(Math.floor(timeLeft));
        setIsExpired(timeLeft < 0);
        
        if (timeLeft < 60) {
          console.warn('Токен истекает через', timeLeft, 'секунд');
        }
      } catch (error) {
        setIsExpired(true);
      }
    };

    checkExpiration();
    const interval = setInterval(checkExpiration, 10000); // Проверять каждые 10 сек

    return () => clearInterval(interval);
  }, []);

  return { isExpired, expiresIn };
}

// Использование
function Dashboard() {
  const { isExpired, expiresIn } = useTokenExpiration();

  if (isExpired) {
    return <Navigate to="/login" />;
  }

  return (
    <div>
      <h1>Панель управления</h1>
      {expiresIn && expiresIn < 300 && (
        <div style={{ backgroundColor: 'yellow' }}>
          Ваш токен истекает через {expiresIn} секунд
        </div>
      )}
    </div>
  );
}

5. Проверка перед каждым запросом

const makeAuthenticatedRequest = async (url, options = {}) => {
  const token = localStorage.getItem('access_token');
  
  // Проверка перед запросом
  if (isTokenExpired(token)) {
    try {
      await refreshAccessToken();
    } catch (error) {
      window.location.href = '/login';
      throw new Error('Токен истек и не может быть обновлен');
    }
  }
  
  const newToken = localStorage.getItem('access_token');
  return fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      Authorization: `Bearer ${newToken}`
    }
  });
};

Лучшие практики

  1. Никогда полностью не полагайся на фронтенд проверку - сервер всегда проверяет токен
  2. Используй refresh токены - они живут дольше и позволяют обновить access token
  3. Обновляй токен автоматически - не ждите, пока пользователь заметит истечение
  4. Обработай 401 ошибки - это способ сервера сказать, что токен невалиден
  5. Предупреждай пользователя - покажи уведомление перед истечением
  6. Закрывай сессию правильно - при логауте удаляй оба токена

Рекомендуемая стратегия

  1. Сохранять access token в памяти (sessionStorage) и refresh token в httpOnly cookie
  2. Обновлять access token за 1 минуту до истечения
  3. При 401 ответе пытаться обновить токен
  4. Если refresh неудачен, отправить на страницу логина
  5. Показывать пользователю уведомление о скором истечении