Ограничение доступа

Navigo — это легковесная библиотека маршрутизации для JavaScript, которая позволяет управлять навигацией в одностраничных приложениях (SPA). Одной из ключевых задач при построении SPA является ограничение доступа к определённым маршрутам в зависимости от состояния пользователя: авторизован или нет, имеет ли он нужные права, роль или подписку. В Navigo это достигается с помощью комбинации условной логики в обработчиках маршрутов и переадресации (redirect).


Настройка базового маршрутизатора

Создание экземпляра Navigo выполняется через конструктор с указанием базового пути и опций:

import Navigo from 'navigo';

const router = new Navigo('/', { hash: true });

Опция hash: true позволяет использовать хеш-маршрутизацию (/#/path), что удобно для SPA без серверной настройки.


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

Защищённый маршрут — это маршрут, доступ к которому должен быть ограничен. В Navigo нет встроенной системы аутентификации, поэтому контроль выполняется через функцию-обработчик маршрута. Пример:

function requireAuth(handler) {
  return function(...params) {
    const user = JSON.parse(localStorage.getItem('user'));
    if (user && user.isLoggedIn) {
      handler(...params);
    } else {
      router.navigate('/login');
    }
  }
}
  • requireAuth — обёртка для маршрутов, проверяющая наличие пользователя и его состояние авторизации.
  • Если проверка не пройдена, выполняется переадресация на страницу логина.
  • Такой подход позволяет использовать один и тот же механизм для всех защищённых маршрутов.

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

router.on('/dashboard', requireAuth(() => {
  document.body.innerHTML = '<h1>Панель управления</h1>';
}));

router.on('/login', () => {
  document.body.innerHTML = '<h1>Страница логина</h1>';
});
  • При переходе на /dashboard проверяется состояние авторизации.
  • Если пользователь не авторизован, происходит редирект на /login.
  • Этот метод можно расширять для ролевого доступа.

Ограничение доступа по ролям

Для SPA с разными уровнями прав доступа необходимо проверять роль пользователя. Можно создать обёртку requireRole:

function requireRole(role, handler) {
  return function(...params) {
    const user = JSON.parse(localStorage.getItem('user'));
    if (user && user.roles.includes(role)) {
      handler(...params);
    } else {
      router.navigate('/forbidden');
    }
  }
}

Пример маршрута с ограничением по роли:

router.on('/admin', requireRole('admin', () => {
  document.body.innerHTML = '<h1>Админ-панель</h1>';
}));

router.on('/forbidden', () => {
  document.body.innerHTML = '<h1>Доступ запрещён</h1>';
});
  • В этом примере доступ к /admin имеют только пользователи с ролью admin.
  • Для остальных выполняется редирект на страницу с сообщением о запрете доступа.

Динамическая проверка доступа

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

router.on('/profile/:userId', requireAuth((params) => {
  const currentUser = JSON.parse(localStorage.getItem('user'));
  if (currentUser.id === params.userId || currentUser.roles.includes('admin')) {
    document.body.innerHTML = `<h1>Профиль пользователя ${params.userId}</h1>`;
  } else {
    router.navigate('/forbidden');
  }
}));
  • Проверяется, что пользователь либо владеет ресурсом, либо имеет административные права.
  • Такой подход позволяет гибко управлять доступом к приватным страницам и данным.

Использование глобального хука before

Navigo поддерживает глобальный хук before, который вызывается перед обработкой любого маршрута:

router.hooks({
  before: (done, params) => {
    const user = JSON.parse(localStorage.getItem('user'));
    if (!user || !user.isLoggedIn) {
      router.navigate('/login');
      done(false); // отменяем обработку текущего маршрута
    } else {
      done(); // продолжаем выполнение маршрута
    }
  }
});
  • done(false) — предотвращает выполнение обработчика маршрута.
  • Этот хук полезен для массовой защиты маршрутов, не оборачивая каждый маршрут отдельной функцией.

Сочетание локальных и глобальных проверок

Для комплексного контроля можно комбинировать обёртки маршрутов с глобальным хуком:

router.on('/dashboard', () => {
  document.body.innerHTML = '<h1>Панель управления</h1>';
});

router.on('/admin', requireRole('admin', () => {
  document.body.innerHTML = '<h1>Админ-панель</h1>';
}));

router.hooks({
  before: (done) => {
    const user = JSON.parse(localStorage.getItem('user'));
    if (!user || !user.isLoggedIn) {
      router.navigate('/login');
      done(false);
    } else {
      done();
    }
  }
});
  • Все маршруты проверяются глобально на авторизацию.
  • Дополнительные ограничения по ролям обрабатываются локально для отдельных маршрутов.

Рекомендации по безопасному хранению состояния

  • Не хранить критически важные данные (например, токены доступа) в localStorage без шифрования.
  • Для SPA лучше использовать HTTP-only cookies или безопасное хранилище с токенами и проверкой на сервере.
  • В Navigo логика ограничения доступа должна работать совместно с серверной проверкой для предотвращения обхода маршрутов через клиентский код.

Выводы по архитектуре ограничения доступа

  1. Обёртки маршрутов (requireAuth, requireRole) — гибкий способ управления доступом к отдельным страницам.
  2. Глобальные хуки (before) — эффективны для массовой проверки авторизации.
  3. Динамические маршруты с параметрами позволяют контролировать доступ к конкретным ресурсам.
  4. Комбинация клиентских проверок с серверной валидацией обеспечивает надёжную защиту SPA.

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