Настройка авторизации в React: полное руководство по JWT и защите маршрутов
23 апреля 2026 г.
Авторизация в React-приложениях чаще всего реализуется через JWT-токены (JSON Web Token). После успешного входа сервер выдаёт токен, клиент хранит его и при каждом запросе к API отправляет в заголовке Authorization. В этом руководстве ты узнаешь, как настроить полный цикл авторизации: от входа до защиты маршрутов и выхода.
Основные понятия JWT
JWT — это открытый стандарт (RFC 7519) для передачи данных между сторонами в виде JSON-объекта. Токен состоит из трёх частей: заголовка, полезной нагрузки и подписи, разделённых точками. После авторизации сервер возвращает JWT, который клиент обязан отправлять при каждом защищённом запросе. В типовой React-схеме токен хранится в localStorage или sessionStorage (либо в httpOnly-куках, если сервер настроен соответствующим образом).
Структура компонентов авторизации
Для удобства всю логику авторизации выносят в отдельный контекст (AuthContext), который предоставляет состояние (user, token, isAuthenticated) и методы login, logout.
AuthContext — глобальное состояние
Контекст позволяет любому компоненту получить доступ к данным пользователя и функциям входа/выхода без пробрасывания пропсов через всё дерево.
Компонент входа (Login)
Форма с полями email/username и пароль. При отправке вызывает API-эндпоинт, получает токен и сохраняет его через контекст.
Защищённый маршрут (PrivateRoute)
Обёртка над Route из React Router, которая проверяет isAuthenticated. Если пользователь не авторизован — перенаправляет на страницу логина.
Настройка авторизации в React по шагам
Рассмотрим практическую реализацию с React Router v6 и функциональными компонентами.
Шаг 1: создание AuthContext
// src/context/AuthContext.jsx
import { createContext, useState, useContext, useEffect } from 'react';
const AuthContext = createContext(null);
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [token, setToken] = useState(localStorage.getItem('token'));
useEffect(() => {
if (token) {
// Опционально: проверить валидность токена
}
}, [token]);
const login = async (email, password) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (data.token) {
localStorage.setItem('token', data.token);
setToken(data.token);
setUser(data.user);
}
return data;
};
const logout = () => {
localStorage.removeItem('token');
setToken(null);
setUser(null);
};
return (
<AuthContext.Provider value={{ user, token, login, logout, isAuthenticated: !!token }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
Шаг 2: функция логина
Компонент Login использует useAuth и перенаправляет после успешного входа:
// src/pages/Login.jsx
import { useState } from 'react';
import { useAuth } from '../context/AuthContext';
import { useNavigate } from 'react-router-dom';
export const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { login } = useAuth();
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
const result = await login(email, password);
if (result.token) {
navigate('/dashboard');
} else {
alert('Ошибка входа');
}
};
return ( <form onSubmit={handleSubmit}>...</form> );
};
Шаг 3: функция логаута
Вызов logout очищает токен и редиректит на логин:
// src/components/LogoutButton.jsx
import { useAuth } from '../context/AuthContext';
import { useNavigate } from 'react-router-dom';
export const LogoutButton = () => {
const { logout } = useAuth();
const navigate = useNavigate();
const handleLogout = () => {
logout();
navigate('/login');
};
return <button onClick={handleLogout}>Выйти</button>;
};
Шаг 4: защита маршрутов
Создадим компонент PrivateRoute для React Router v6:
// src/components/PrivateRoute.jsx
import { Navigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
export const PrivateRoute = ({ children }) => {
const { isAuthenticated } = useAuth();
return isAuthenticated ? children : <Navigate to="/login" />;
};
Пример использования в App.jsx:
// src/App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './context/AuthContext';
import { PrivateRoute } from './components/PrivateRoute';
import { Login } from './pages/Login';
import { Dashboard } from './pages/Dashboard';
function App() {
return (
<BrowserRouter>
<AuthProvider>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={
<PrivateRoute><Dashboard /></PrivateRoute>
} />
</Routes>
</AuthProvider>
</BrowserRouter>
);
}
Шаг 5: добавление токена в запросы
Для автоматического добавления токена во все запросы создай перехватчик (interceptor) для fetch или используй axios:
// src/api/axios.js
import axios from 'axios';
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL
});
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export default api;
Хранение токенов и безопасность
localStorage уязвим для XSS-атак: злоумышленник может внедрить скрипт и украсть токен. Более безопасный способ — хранить токен в httpOnly-куках с флагом Secure и SameSite=Strict. Однако такой подход усложняет клиентскую логику: JavaScript не имеет доступа к куке, и её чтением занимается сервер автоматически. В большинстве внутренних и средних проектов допустимо хранение в localStorage при условии строгой политики CSP (Content Security Policy).
Обработка ошибок и защита от неавторизованных запросов
При получении от API статуса 401 (Unauthorized) рекомендуется автоматически выполнять логаут и перенаправлять пользователя на страницу входа. В axios-перехватчике это выглядит так:
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
Частые ошибки при настройке авторизации
- Неправильный заголовок: сервер ожидает
Authorization: Bearer <token>, а отправленAuthorization: Token <token>или без пробела. - Отсутствие обработки обновления токена (refresh token): при истечении срока JWT пользователь вылетает. Реализуй механизм refresh-токена или настрой долгоживущие токены (с осторожностью).
- Сохранение токена только в памяти (useState): после перезагрузки страницы авторизация теряется. Всегда синхронизируй токен с localStorage.
- Защита только на фронтенде: защищённые маршруты в React — это лишь UX-улучшение. Все критичные данные должны проверяться на сервере.
- Не учитывается роль/права: если пользователь сменил роль (например, его заблокировали), фронт узнает об этом только после перезагрузки. Добавь периодическую проверку токена или механизм invalidate.
Заключение
Ты настроил авторизацию в React: создал контекст для состояния, реализовал логин/логаут, защитил приватные маршруты и добавил токен в запросы. Для production-проектов дополнительно продумай безопасное хранение токенов, обработку 401 ошибок и refresh-механизм. Описанный паттерн — стандарт для большинства React-приложений, работающих с REST API и JWT.
Что почитать дальше
Дополнительные материалы из архива, которые могут быть полезны после этой статьи.
Правила использования React хуков: как писать код без ошибок
React хуки изменили подход к разработке компонентов. Разбираем главные правила их использования: вызов только на верхне…
Читать далее
Настройка ESLint для React приложения: полное руководство
Пошаговое руководство по настройке ESLint в React-проектах. Установка, конфигурация, популярные плагины, интеграция с I…
Читать далее
Как деплоить React приложение: от сборки до продакшена
Подробный гайд по деплою React приложений: от создания оптимизированной сборки до размещения на сервере. Рассматриваем…
Читать далее