Добавим JWT в наше приложение
Теперь, когда мы умеем управлять транзакциями и подключать данные через API, пора сделать шаг к персонализации и защите данных. Ведь если каждый пользователь будет видеть чужие расходы — это вряд ли можно назвать "финансовым приложением".
Именно здесь на сцену выходит JWT — JSON Web Token. Это способ удостовериться, что пользователь — это он сам, и никто другой. С помощью токенов мы создадим систему авторизации, где каждый будет работать только со своими данными.
Что такое JWT?
JWT (JSON Web Token) — это способ удостоверения пользователя. После успешного входа в систему сервер создаёт токен и отправляет его клиенту. Этот токен хранится на клиенте и передаётся с каждым запросом, чтобы сервер мог проверить: "Да, это тот самый пользователь".
Шаг 1: установка необходимых библиотек
Всё, что нам нужно, это:
npm install axios jwt-decode
- axios — для отправки запросов;
- jwt-decode — чтобы расшифровать токен и узнать, кто вошёл.
Шаг 2: создание AuthService
Создадим сервис, который будет отправлять данные пользователя на сервер и получать в ответ токен.
Файл src/services/AuthService.ts
import axios from 'axios';
// Базовый адрес вашего API
const API_URL = 'https://your-api.com/api';
// Отправка логина и пароля — получаем JWT
export const login = async (email: string, password: string): Promise<string> => {
const response = await axios.post(`${API_URL}/auth/login`, {
email,
password,
});
return response.data.token; // Сервер возвращает токен
};
// Регистрация пользователя
export const register = async (email: string, password: string): Promise<void> => {
await axios.post(`${API_URL}/auth/register`, {
email,
password,
});
};
Что происходит здесь:
loginотправляет учётные данные, получает и возвращает JWT.registerсоздаёт нового пользователя.
Шаг 3: создание хука useAuth
Чтобы React-компоненты знали, кто сейчас в системе, и могли управлять авторизацией, создадим кастомный хук.
Файл src/hooks/useAuth.ts
import { useState } from 'react';
import jwtDecode from 'jwt-decode';
// Типизация пользователя
interface User {
id: string;
email: string;
}
// Хук для работы с авторизацией
export const useAuth = () => {
const [user, setUser] = useState<User | null>(null);
// Сохраняем токен и устанавливаем пользователя
const login = (token: string) => {
localStorage.setItem('token', token);
const decoded: any = jwtDecode(token); // Декодируем токен
setUser({ id: decoded.userId, email: decoded.email });
};
// Удаляем токен и сбрасываем пользователя
const logout = () => {
localStorage.removeItem('token');
setUser(null);
};
// Получение текущего пользователя
const getUser = () => user;
return { user, login, logout, getUser };
};
Заметка: здесь мы храним токен в localStorage, что удобно, но есть и риски безопасности. В реальных проектах лучше использовать HttpOnly cookies.
Шаг 4: создание защищённого маршрута
Теперь мы можем защитить любую страницу: если пользователь не авторизован — перенаправим его на /login.
Файл src/components/PrivateRoute.tsx
import React from 'react';
import { Navigate } from 'react-router-dom';
interface PrivateRouteProps {
children: React.ReactNode;
}
// Компонент-обёртка: проверяет, есть ли токен
const PrivateRoute: React.FC<PrivateRouteProps> = ({ children }) => {
const token = localStorage.getItem('token');
return token ? <>{children}</> : <Navigate to="/login" />;
};
export default PrivateRoute;
Обратите внимание: здесь логика максимально простая — мы просто проверяем наличие токена. В более сложных случаях можно проверять срок действия токена, делать валидацию на стороне сервера и пр.
Шаг 5: обновим маршруты приложения
Теперь мы можем сделать, например, главную страницу (/dashboard) доступной только авторизованным пользователям.
Файл src/App.tsx
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import PrivateRoute from './components/PrivateRoute';
import LoginPage from './pages/LoginPage';
import DashboardPage from './pages/DashboardPage';
const App: React.FC = () => {
return (
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/dashboard"
element={
<PrivateRoute>
<DashboardPage />
</PrivateRoute>
}
/>
</Routes>
</Router>
);
};
export default App;
Что мы сделали:
- Если пользователь не авторизован, он будет перенаправлен на
/login. - После входа он попадает на
/dashboard.
Шаг 6: страница входа
Наконец, сделаем форму, в которой пользователь сможет войти в систему.
Файл src/pages/LoginPage.tsx
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { login as loginService } from '../services/AuthService';
import { useAuth } from '../hooks/useAuth';
const LoginPage: React.FC = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { login } = useAuth();
const navigate = useNavigate();
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
try {
const token = await loginService(email, password);
login(token); // Сохраняем токен и пользователя
navigate('/dashboard'); // Перенаправляем на защищённую страницу
} catch (error) {
console.error('Ошибка входа:', error);
alert('Неверный email или пароль');
}
};
return (
<form onSubmit={handleLogin}>
<h1>Вход</h1>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="password"
placeholder="Пароль"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit">Войти</button>
</form>
);
};
export default LoginPage;
Ключевые моменты:
- При успешном логине токен сохраняется.
- Пользователь перенаправляется на защищённую страницу (
/dashboard). - При ошибке — показывается уведомление.
Вот и всё! Мы создали базовые защищённые маршруты с JWT и обеспечили аутентификацию пользователей.
Частые ошибки и подводные камни
- Хранение токена в localStorage. Это удобно, но не идеально безопасно — токен могут украсть с помощью XSS-атак.
- Просроченные токены. Убедитесь, что сервер возвращает ошибку 401 при истекших токенах.
- Обновление токена (refresh). Настройте механизм обновления токена, чтобы избежать разлогинов через короткое время.
Для лучшей безопасности можно хранить токен в cookies с флагом HttpOnly и настроить сервер для работы с ними.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ