Аутентификация и авторизация

Аутентификация и авторизация — ключевые аспекты безопасности веб-приложений. Аутентификация отвечает за подтверждение личности пользователя, авторизация — за определение его прав доступа к ресурсам и функционалу. В контексте Riot.js эти процессы реализуются через взаимодействие компонентов, состояния приложения и внешних API.


Организация состояния пользователя

Для управления состоянием аутентификации используется глобальное хранилище или контекст. В Riot.js можно создать отдельный store, который будет хранить текущего пользователя, токен и статус авторизации:

import { observable } from 'riot';

export const authStore = observable({
  user: null,
  token: null,
  isAuthenticated: false,
  
  login(userData, token) {
    this.user = userData;
    this.token = token;
    this.isAuthenticated = true;
    this.trigger('change');
  },
  
  logout() {
    this.user = null;
    this.token = null;
    this.isAuthenticated = false;
    this.trigger('change');
  }
});

Ключевой момент: использование observable позволяет компонентам автоматически реагировать на изменения состояния пользователя.


Реализация формы входа

Форма логина в Riot.js строится на основе компонентов и биндинга данных. Пример простого компонента:

<login-form>
  <form onsub mit={handleLogin}>
    <input type="text" placeholder="Email" ref="email" required />
    <input type="password" placeholder="Пароль" ref="password" required />
    <button type="submit">Войти</button>
  </form>

  <script>
    import { authStore } from './authStore.js';

    function handleLogin(e) {
      e.preventDefault();
      const email = this.refs.email.value;
      const password = this.refs.password.value;

      // Пример вызова API аутентификации
      fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      })
      .then(res => res.json())
      .then(data => {
        if (data.token) {
          authStore.login(data.user, data.token);
        } else {
          console.error('Ошибка аутентификации');
        }
      });
    }
  </script>
</login-form>

Особенности реализации:

  • Использование refs для доступа к полям формы.
  • Обновление глобального состояния через authStore.
  • Поддержка реактивного интерфейса через события observable.

Авторизация компонентов

Авторизация в Riot.js строится на проверке состояния пользователя перед отображением компонентов. Для этого создаются условные рендеры и защитные механизмы:

<private-section>
  <script>
    import { authStore } from './authStore.js';
    let isAuthenticated = authStore.isAuthenticated;

    authStore.on('change', () => {
      isAuthenticated = authStore.isAuthenticated;
      update();
    });
  </script>

  <div if={isAuthenticated}>
    Доступ к приватной информации
  </div>
  <div if={!isAuthenticated}>
    Пожалуйста, войдите в систему
  </div>
</private-section>

Принципы защиты компонентов:

  • Компонент подписан на изменения состояния authStore.
  • Используется условный рендер if={} для контроля доступа.
  • Обновление интерфейса происходит автоматически при изменении статуса авторизации.

Работа с JWT и токенами

Для безопасной аутентификации рекомендуется использовать JSON Web Token (JWT). Основные шаги:

  1. Получение токена при логине через API.
  2. Сохранение токена в памяти или localStorage.
  3. Отправка токена в заголовке Authorization при запросах к защищённым ресурсам:
fetch('/api/protected', {
  headers: {
    'Authorization': `Bearer ${authStore.token}`
  }
})
.then(res => res.json())
.then(data => console.log(data));

Важные моменты:

  • Токен должен храниться безопасно, избегать XSS-уязвимостей.
  • JWT позволяет серверу валидировать пользователя без постоянного хранения сессий.

Обработка выхода из системы

Выход из системы реализуется через метод logout глобального состояния. После вызова компонента интерфейс автоматически скрывает приватные данные:

<button oncl ick={() => authStore.logout()}>Выйти</button>

Эффекты:

  • authStore.user сбрасывается.
  • Компоненты, зависящие от isAuthenticated, обновляются.
  • Токен может быть удалён из localStorage.

Роли и права доступа

Для сложных приложений требуется разграничение доступа по ролям. Пример расширенного состояния:

export const authStore = observable({
  user: null,
  roles: [],
  isAuthenticated: false,

  hasRole(role) {
    return this.roles.includes(role);
  },

  login(userData, token) {
    this.user = userData;
    this.roles = userData.roles || [];
    this.isAuthenticated = true;
    this.trigger('change');
  }
});

В компонентах проверка ролей позволяет гибко управлять доступом:

<div if={authStore.hasRole('admin')}>
  Административная панель
</div>

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

Для защиты маршрутов в приложениях с Riot.js используется проверка состояния перед переходом:

import { authStore } from './authStore.js';
import router from 'riot-route';

router.route('/dashboard', () => {
  if (!authStore.isAuthenticated) {
    router.redirect('/login');
  } else {
    mountDashboard();
  }
});

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

  • Редирект на страницу логина при отсутствии авторизации.
  • Защита всех приватных маршрутов централизованно.
  • Поддержка динамических прав доступа через проверку ролей.

Рекомендации по безопасности

  • Не хранить пароли на клиенте.
  • Всегда валидировать токены на сервере.
  • Использовать HTTPS для передачи конфиденциальных данных.
  • Регулярно обновлять и проверять библиотеки безопасности.

Реактивность интерфейса

Сочетание observable и условного рендера позволяет создавать полностью реактивную систему аутентификации. Любое изменение состояния пользователя мгновенно отражается на видимости компонентов и функционале приложения, обеспечивая удобство и безопасность.