← Назад к вопросам
Как понимаешь в какой момент истек токен?
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}`
}
});
};
Лучшие практики
- Никогда полностью не полагайся на фронтенд проверку - сервер всегда проверяет токен
- Используй refresh токены - они живут дольше и позволяют обновить access token
- Обновляй токен автоматически - не ждите, пока пользователь заметит истечение
- Обработай 401 ошибки - это способ сервера сказать, что токен невалиден
- Предупреждай пользователя - покажи уведомление перед истечением
- Закрывай сессию правильно - при логауте удаляй оба токена
Рекомендуемая стратегия
- Сохранять access token в памяти (sessionStorage) и refresh token в httpOnly cookie
- Обновлять access token за 1 минуту до истечения
- При 401 ответе пытаться обновить токен
- Если refresh неудачен, отправить на страницу логина
- Показывать пользователю уведомление о скором истечении