Куда прописываешь токен?
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
Места размещения токена авторизации в Frontend-приложениях
Как Frontend Developer с многолетним опытом, я рассматриваю размещение токена авторизации (чаще всего JWT — JSON Web Token) как критически важный архитектурный выбор, который напрямую влияет на безопасность, UX и производительность приложения. Токен — это ключ, открывающий доступ к API и защищенным ресурсам, и его необходимо хранить и передавать с максимальной осторожностью.
Основные подходы к хранению токена на клиенте
1. Local Storage / Session Storage
Наиболее простой, но и наиболее рискованный способ. Токен сохраняется в браузерном хранилище, которое доступно через JavaScript.
// Сохранение токена после успешной авторизации
const saveTokenToLocalStorage = (token) => {
localStorage.setItem('auth_token', token);
};
// Получение токена для использования в запросах
const getTokenFromLocalStorage = () => {
return localStorage.getItem('auth_token');
};
Проблемы этого метода:
- Уязвимость к XSS (Cross-Site Scripting): Если в приложение внедрен вредоносный скрипт, он может легко получить токен из
localStorage. - Токен не имеет "срока жизни" в контексте сессии браузера — остается после закрытия окна (в отличие от
sessionStorage).
2. HTTP-only Cookies
Более безопасный подход. Токен устанавливается сервером в HTTP-only cookie, который недоступен для JavaScript на клиенте.
// Серверный код (пример, Node.js/Express)
app.post('/login', (req, res) => {
// ... проверка логина/пароля
const token = generateJWT(user);
// Установка токена в HTTP-only cookie
res.cookie('auth_token', token, {
httpOnly: true, // Недоступно через JS
secure: true, // Только через HTTPS
sameSite: 'strict' // Защита от CSRF
});
res.json({ success: true });
});
Преимущества:
- Защита от XSS: JavaScript не может прочитать этот cookie, следовательно, вредоносный скрипт не сможет его украсть.
- Автоматическая отправка: Cookie автоматически прикрепляется к каждому запросу на тот же домен, не требуя явной логики на фронтенде.
- Срок жизни контролируется параметрами cookie (
expires,maxAge).
Недостатки/сложности:
- Уязвимость к CSRF (Cross-Site Request Forgery): Необходимо дополнительно защищаться (используя
sameSiteатрибуты, CSRF-токены). - Сложнее работать с CORS: При обращении к API на другом домене/поддомене нужно явно настроить политику
credentials. - Клиентский код не имеет прямого доступа к токену, что может затруднить некоторые сценарии (например, добавление его в заголовок для запроса к стороннему API).
3. Memory (In-memory storage) / State
Токен хранится исключительно в памяти приложения (например, в переменной, в состоянии React/Vue/и т.д.).
// Пример в React с использованием контекста или состояния
const AuthContext = createContext();
const AuthProvider = ({ children }) => {
const [token, setToken] = useState(null);
const login = async (credentials) => {
const response = await fetch('/api/login', { ... });
const { token } = await response.json();
// Сохраняем только в памяти, НЕ в localStorage!
setToken(token);
};
return (
<AuthContext.Provider value={{ token, setToken }}>
{children}
</AuthContext.Provider>
);
};
Преимущества:
- Максимальная безопасность: Токен исчезает при закрытии или перезагрузке страницы. Не сохраняется нигде перманентно, недоступен для XSS.
- Идеально для SPA с высокими требованиями безопасности.
Недостатки:
- Плохой UX: Пользователь теряет авторизацию после каждого закрытия браузера или даже при простой перезагрузке страницы.
- Необходимость частого повторного логина.
Где и как "прописывается" токен при отправке запросов?
Независимо от места хранения, для авторизации запросов к API токен необходимо включить в HTTP-заголовки.
Стандартный подход: заголовок Authorization
// Функция для создания авторизованного запроса
const makeAuthenticatedRequest = async (url, options = {}) => {
const token = getToken(); // Получаем из памяти, контекста или (осторожно!) из localStorage
const headers = {
...options.headers,
'Authorization': `Bearer ${token}` // Классическая схема Bearer Token
};
return fetch(url, {
...options,
headers
});
};
// Использование
makeAuthenticatedRequest('/api/protected-data', {
method: 'GET'
}).then(response => response.json());
Если токен в HTTP-only Cookie:
Заголовок Authorization часто не требуется, так как cookie автоматически отправляется браузером. Однако, для явного контроля или работы с CORS, его можно установить.
// Для запросов к API на другом домене необходимо явно включить credentials
fetch('https://another-domain.com/api/data', {
method: 'GET',
credentials: 'include' // Это позволит браузеру прикрепить HTTP-only cookie
});
Современные best practices и рекомендации
На практике я часто использую гибридный или компромиссный подход, зависящий от требований приложения:
- Для большинства корпоративных SPA: HTTP-only cookie + Secure + SameSite=Strict/Lax. Это обеспечивает хороший баланс безопасности (защита от XSS) и удобства (сессия сохраняется между перезагрузками, если cookie имеет достаточный
maxAge). Необходимо дополнить защитой от CSRF. - Для мобильных гибридных приложений или микросервисных архитектур: Часто используется Bearer Token в заголовке
Authorization, хранящийся в защищенном memory storage или в специализированных хранилищах, предоставляемых фреймворками (например, Secure Storage в React Native). - Никогда не храните токены (особенно refresh tokens) в
localStorageбез крайней необходимости и без дополнительных мер защиты (например, шифрования, которое само по себе сложно реализовать безопасно на клиенте).
Ключевой вывод: "Прописывание" токена — это не одна точка в коде. Это архитектурная цепочка: место безопасного хранения (cookie, память), механизм его получения (из контекста, из cookie) и способ его передачи в запросах (заголовок Authorization, автоматическая отправка cookie). Выбор зависит от триады Security — UX — Architecture вашего конкретного проекта.