Ролевая модель доступа

React Router предоставляет гибкий инструмент для управления навигацией в приложениях на React. Одной из ключевых задач является организация ролевого доступа к различным маршрутам. Это позволяет ограничивать доступ к страницам в зависимости от роли пользователя (например, администратор, модератор, обычный пользователь).

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

Для реализации ролевого доступа необходимо создать компонент-обёртку (ProtectedRoute), который проверяет роль пользователя перед отображением целевого компонента. Пример базового подхода:

import { Navigate } from 'react-router-dom';

function ProtectedRoute({ userRole, allowedRoles, children }) {
  if (!allowedRoles.includes(userRole)) {
    return <Navigate to="/unauthorized" replace />;
  }
  return children;
}

Ключевые моменты:

  • userRole — текущая роль пользователя, обычно берётся из состояния приложения или контекста авторизации.
  • allowedRoles — массив ролей, которым разрешён доступ к маршруту.
  • Navigate используется для перенаправления на страницу ошибки или авторизации при несоответствии роли.

Интеграция с маршрутизатором

Для использования ProtectedRoute маршруты объявляются следующим образом:

import { Routes, Route } from 'react-router-dom';

<Routes>
  <Route path="/admin" element={
    <ProtectedRoute userRole={currentUser.role} allowedRoles={['admin']}>
      <AdminPanel />
    </ProtectedRoute>
  } />
  <Route path="/profile" element={
    <ProtectedRoute userRole={currentUser.role} allowedRoles={['user', 'admin']}>
      <UserProfile />
    </ProtectedRoute>
  } />
  <Route path="/unauthorized" element={<UnauthorizedPage />} />
</Routes>

Особенности:

  • Компонент-обёртка позволяет централизованно управлять доступом.
  • Структура Routes и Route остаётся стандартной, что облегчает поддержку.
  • element передаётся непосредственно JSX-компонент, обёрнутый проверкой роли.

Использование контекста для ролей

Для упрощения передачи информации о пользователе и его роли удобно использовать React Context:

import { createContext, useContext } from 'react';

const AuthContext = createContext(null);

export function AuthProvider({ children, user }) {
  return <AuthContext.Provider value={user}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  return useContext(AuthContext);
}

Далее ProtectedRoute может быть переписан так, чтобы не требовать явного userRole:

function ProtectedRoute({ allowedRoles, children }) {
  const user = useAuth();
  if (!user || !allowedRoles.includes(user.role)) {
    return <Navigate to="/unauthorized" replace />;
  }
  return children;
}

Динамические маршруты с ограничением доступа

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

<Route path="/profile/:id" element={
  <ProtectedRoute allowedRoles={['admin', 'user']}>
    <UserProfile />
  </ProtectedRoute>
} />

Внутри компонента UserProfile можно дополнительно проверять соответствие текущего пользователя параметру id:

import { useParams } from 'react-router-dom';
import { useAuth } from './AuthContext';

function UserProfile() {
  const { id } = useParams();
  const user = useAuth();

  if (user.role !== 'admin' && user.id !== id) {
    return <UnauthorizedPage />;
  }

  return <div>Профиль пользователя {id}</div>;
}

Роль административного доступа на уровне приложения

Для более сложных приложений часто создаётся система ролей и прав, где каждая роль имеет набор разрешений. Это позволяет:

  • Масштабировать систему доступа без дублирования ProtectedRoute.
  • Использовать разрешения на уровне компонентов и действий, а не только маршрутов.

Пример проверки разрешений:

function ProtectedRoute({ requiredPermission, children }) {
  const user = useAuth();
  if (!user || !user.permissions.includes(requiredPermission)) {
    return <Navigate to="/unauthorized" replace />;
  }
  return children;
}

Советы по организации ролевого доступа

  • Централизация правил: хранить все роли и разрешения в одном месте, чтобы легко менять политику безопасности.
  • Комбинация ролей и параметров: проверять не только роль, но и условия (например, принадлежность к ресурсу).
  • Управление перенаправлениями: всегда определять страницу для случаев отказа в доступе (/unauthorized), чтобы не было “мертвых зон”.
  • Минимизация дублирования: использовать обёртки и контекст, чтобы не проверять роль в каждом компоненте вручную.

Эта структура обеспечивает гибкое и безопасное управление доступом, полностью интегрированное с React Router, и подходит как для небольших приложений, так и для крупных корпоративных проектов с многоуровневой системой ролей.