JavaRush /Курсы /Модуль 3: React /Создание защищённых маршрутов и авторизации с JWT

Создание защищённых маршрутов и авторизации с JWT

Модуль 3: React
19 уровень , 4 лекция
Открыта

Добавим 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 и настроить сервер для работы с ними.

2
Задача
Модуль 3: React, 19 уровень, 4 лекция
Недоступна
Создание AuthService
Создание AuthService
3
Опрос
Постановка задачи для проекта, 19 уровень, 4 лекция
Недоступен
Постановка задачи для проекта
Постановка задачи для проекта
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ