JavaRush /Курсы /Модуль 3: React /Типизация защищённых маршрутов с TypeScript

Типизация защищённых маршрутов с TypeScript

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

Задача: создать защищённые маршруты

В защищённых маршрутах нас будет интересовать:

  1. Проверка, авторизован ли пользователь.
  2. Перенаправление неавторизованных пользователей на страницу логина.
  3. Типизация данных о пользователе и логики проверки авторизации.

Стартовый код

Создадим проект и убедимся, что у нас установлены все необходимые зависимости:

npx create-react-app protected-routes --template typescript
cd protected-routes
npm install react-router-dom

Также создадим имитацию сервиса авторизации. Например, сделаем простой токен для проверки:

// src/auth/AuthService.ts

interface User {
  name: string;
  role: 'admin' | 'user';
}

// Импровизированный токен
const fakeToken = '123secureToken';

export const AuthService = {
  isAuthenticated: false,
  user: null as User | null,

  login(token: string): boolean {
    if (token === fakeToken) {
      this.isAuthenticated = true;
      this.user = { name: 'John Doe', role: 'admin' }; // Пример пользователя.
      return true;
    }
    return false;
  },

  logout() {
    this.isAuthenticated = false;
    this.user = null;
  },

  getUser() {
    return this.user;
  }
};

Этот код эмулирует очень простую логику аутентификации. А теперь создадим защищённый маршрут.

Реализация защищённого маршрута

Создадим компонент ProtectedRoute, который будет проверять, авторизован ли пользователь:

// src/routes/ProtectedRoute.tsx
import React from 'react';
import { Navigate } from 'react-router-dom';
import { AuthService } from '../auth/AuthService';

interface ProtectedRouteProps {
  children: JSX.Element;
  redirectPath?: string;
}

export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
  children,
  redirectPath = '/login',
}) => {
  if (!AuthService.isAuthenticated) {
    return <Navigate to={redirectPath} />;
  }
  return children;
};

Объяснение кода:

  1. ProtectedRouteProps описывает пропсы компонента:

    • children: компонент, который нужно отобразить, если пользователь авторизован.
    • redirectPath: путь перенаправления для неавторизованных пользователей (по умолчанию /login).
  2. Если пользователь не авторизован, мы перенаправляем его на страницу логина с помощью компонента Navigate.

Типизация данных о пользователе

Давайте сделаем наш проект ещё более типизированным, добавив интерфейсы для описания данных пользователя.

// src/auth/AuthService.ts

export interface User {
  name: string;
  role: 'admin' | 'user';
}

// Обновим AuthService:
export const AuthService = {
  isAuthenticated: false,
  user: null as User | null,

  getUser(): User | null {
    return this.user;
  },

  // ...остальные методы без изменений.
};

Теперь мы гарантируем, что где бы ни использовались данные пользователя, TypeScript проверит их структуру. Например, при типизации компонентов.

Пример использования защищённого маршрута

Добавим маршруты и защитим доступ к одной из страниц:

// src/App.tsx
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { ProtectedRoute } from './routes/ProtectedRoute';
import { AuthService } from './auth/AuthService';

// Страницы
const Home = () => <h1>Добро пожаловать на главную!</h1>;
const Login = () => {
  const handleLogin = () => {
    AuthService.login('123secureToken');
    window.location.href = '/dashboard'; // Перенаправление после входа.
  };

  return (
    <div>
      <h1>Вход</h1>
      <button onClick={handleLogin}>Авторизоваться</button>
    </div>
  );
};
const Dashboard = () => <h1>Личный кабинет</h1>;

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/login" element={<Login />} />
        <Route
          path="/dashboard"
          element={
            <ProtectedRoute>
              <Dashboard />
            </ProtectedRoute>
          }
        />
      </Routes>
    </Router>
  );
}

export default App;

Что здесь происходит:

  1. Мы создали три страницы:

    • /: Главная страница.
    • /login: Страница входа.
    • /dashboard: Личный кабинет. Доступна только авторизованным пользователям.
  2. Для маршрута /dashboard используется компонент ProtectedRoute. Он проверяет, авторизован ли пользователь.

Дополнительная типизация

Мы можем типизировать компонент Dashboard, чтобы он отображал данные текущего пользователя:

// src/pages/Dashboard.tsx
import React from 'react';
import { AuthService } from '../auth/AuthService';

const Dashboard: React.FC = () => {
  const user = AuthService.getUser();

  return (
    <div>
      <h1>Личный кабинет</h1>
      {user && (
        <p>
          Добро пожаловать, <strong>{user.name}</strong>! Ваша роль: {user.role}.
        </p>
      )}
    </div>
  );
};

export default Dashboard;

Теперь страница личного кабинета отображает данные текущего пользователя.

Особенности и ошибки

Стоит помнить, что типизация защищённых маршрутов напрямую зависит от структуры данных аутентификации. Если структура изменится, TypeScript сразу сообщит о несоответствиях, что поможет предотвратить баги ещё на этапе разработки.

Частая ошибка — забыть передать children в ProtectedRoute. Ещё одна типичная проблема: хранение токенов в localStorage вместо httpOnly куки, что делает приложение уязвимым к XSS-атакам. Мы рассмотрим эти аспекты в последующих лекциях.

1
Задача
Модуль 3: React, 9 уровень, 8 лекция
Недоступна
Настройка защищённого маршрута
Настройка защищённого маршрута
1
Задача
Модуль 3: React, 9 уровень, 8 лекция
Недоступна
Добавление типизации для пользователя
Добавление типизации для пользователя
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ