Защищённые маршруты и аутентификация

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

Для управления состоянием чаще всего используется объект store или глобальный сервис, который хранит данные пользователя:

import { observable } from 'riot';

export const authStore = observable({
  user: null,
  token: null,
  
  login(userData, token) {
    this.user = userData;
    this.token = token;
    localStorage.setItem('token', token);
  },
  
  logout() {
    this.user = null;
    this.token = null;
    localStorage.removeItem('token');
  },
  
  isAuthenticated() {
    return !!this.token;
  }
});

Здесь authStore выполняет роль центра управления аутентификацией. Метод isAuthenticated() позволяет компонентам или маршрутам проверять состояние пользователя.


Настройка защищённых маршрутов

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

Пример конфигурации маршрутов с проверкой:

import { route } from 'riot';
import { authStore } from './authStore';
import Home from './components/Home.riot';
import Dashboard from './components/Dashboard.riot';
import Login from './components/Login.riot';

route('/', () => route('/home'));
route('/home', () => mount(Home));
route('/login', () => mount(Login));

route('/dashboard', () => {
  if (authStore.isAuthenticated()) {
    mount(Dashboard);
  } else {
    route('/login');
  }
});

В этом примере путь /dashboard доступен только авторизованному пользователю. Если проверка не проходит, происходит перенаправление на страницу входа.


Реализация компонента Login

Компонент входа должен обрабатывать форму, отправлять данные на сервер и обновлять состояние authStore после успешной аутентификации:

<login>
  <form onsub mit={handleLogin}>
    <input type="text" placeholder="Email" ref="email">
    <input type="password" placeholder="Password" ref="password">
    <button type="submit">Войти</button>
  </form>

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

    export default {
      handleLogin(event) {
        event.preventDefault();
        const email = this.refs.email.value;
        const password = this.refs.password.value;

        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);
            route('/dashboard');
          } else {
            alert('Неверные данные');
          }
        });
      }
    }
  </script>
</login>

Метод handleLogin обновляет глобальное состояние и перенаправляет пользователя на защищённый маршрут после успешного входа.


Хранение токена и защита сессии

Токен обычно хранится в localStorage или sessionStorage, что позволяет сохранять авторизацию между перезагрузками страницы. Для более высокой безопасности следует использовать HttpOnly cookie на сервере и проверять токен через API.

Пример проверки при загрузке приложения:

const token = localStorage.getItem('token');
if (token) {
  fetch('/api/me', {
    headers: { 'Authorization': `Bearer ${token}` }
  })
  .then(res => res.json())
  .then(user => authStore.login(user, token))
  .catch(() => authStore.logout());
}

Такой подход обеспечивает восстановление сессии при обновлении страницы и корректное состояние защищённых маршрутов.


Динамическое отображение компонентов в зависимости от аутентификации

Riot.js позволяет управлять видимостью элементов через условные рендеры. Это удобно для создания меню и интерфейсов, доступных только авторизованным пользователям:

<nav>
  <ul>
    <li if={!authStore.isAuthenticated()}><a href="/login">Вход</a></li>
    <li if={authStore.isAuthenticated()}><a href="/dashboard">Панель</a></li>
    <li if={authStore.isAuthenticated()}><button oncl ick={logout}>Выход</button></li>
  </ul>

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

    export default {
      logout() {
        authStore.logout();
        route('/home');
      }
    }
  </script>
</nav>

Условные атрибуты if позволяют отображать или скрывать элементы без перезагрузки страницы, основываясь на состоянии authStore.


Защита API-запросов

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

fetch('/api/protected', {
  headers: { 'Authorization': `Bearer ${authStore.token}` }
})
.then(res => {
  if (res.status === 401) {
    authStore.logout();
    route('/login');
  }
  return res.json();
})
.then(data => console.log(data));

Это предотвращает неавторизованный доступ и автоматически перенаправляет пользователя при истечении сессии.


Эта структура обеспечивает комплексную систему аутентификации и управления защищёнными маршрутами в приложении на Riot.js, сохраняя гибкость компонентов и централизованное управление состоянием.