Проверка прав доступа

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

В Page.js проверка прав доступа реализуется через middleware-функции, которые вызываются перед переходом на маршрут. Middleware позволяет перехватывать навигацию и выполнять логику проверки.


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

Middleware в Page.js — это функция с сигнатурой (ctx, next), где:

  • ctx — объект контекста маршрута, содержащий информацию о пути, параметрах, query-параметрах и пользовательском состоянии.
  • next — функция, которая продолжает выполнение следующего middleware или маршрута.

Пример базового middleware для проверки авторизации:

function requireAuth(ctx, next) {
    const user = ctx.state.user; // состояние пользователя
    if (!user || !user.isLoggedIn) {
        page('/login'); // перенаправление на страницу входа
    } else {
        next(); // доступ разрешён, продолжаем навигацию
    }
}

Применение middleware к маршруту:

page('/dashboard', requireAuth, () => {
    renderDashboard();
});

В этом примере, если пользователь не авторизован, он будет перенаправлен на /login. Если авторизован, выполняется функция renderDashboard().


Управление правами на основе ролей

Для более сложных приложений часто требуется проверять роли пользователя. Допустим, есть роли admin, editor и viewer. Middleware для проверки роли может выглядеть так:

function requireRole(role) {
    return function(ctx, next) {
        const user = ctx.state.user;
        if (!user || !user.roles.includes(role)) {
            page('/not-authorized'); // доступ запрещён
        } else {
            next();
        }
    };
}

Использование:

page('/admin', requireAuth, requireRole('admin'), () => {
    renderAdminPanel();
});

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


Глобальные проверки доступа

В Page.js можно регистрировать middleware для всех маршрутов, что удобно для проверки авторизации на уровне всего приложения. Пример:

page('*', (ctx, next) => {
    ctx.state.user = getCurrentUser(); // получение состояния пользователя
    next();
});

Здесь getCurrentUser() может получать данные из localStorage, cookie или API. Этот middleware обеспечит наличие актуального состояния пользователя в каждом маршруте.

После этого можно комбинировать глобальные и локальные middleware:

page('*', (ctx, next) => {
    ctx.state.user = getCurrentUser();
    next();
});

page('/profile', requireAuth, () => {
    renderProfile();
});

page('/admin', requireAuth, requireRole('admin'), () => {
    renderAdminPanel();
});

Обработка асинхронной проверки

Иногда проверка прав доступа требует обращения к серверу, например, для получения актуальных данных о сессии. В Page.js middleware поддерживает асинхронные операции через промисы:

function asyncRequireAuth(ctx, next) {
    return fetch('/api/session')
        .then(response => response.json())
        .then(data => {
            if (!data.isLoggedIn) {
                page('/login');
            } else {
                ctx.state.user = data.user;
                next();
            }
        })
        .catch(() => page('/login'));
}

Использование асинхронного middleware гарантирует, что переход на маршрут произойдёт только после завершения проверки состояния пользователя.


Условное перенаправление на основе состояния

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

function redirectByRole(ctx, next) {
    const user = ctx.state.user;
    if (!user) {
        page('/login');
    } else if (user.roles.includes('admin')) {
        page('/admin');
    } else {
        page('/dashboard');
    }
}

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


Рекомендации по организации middleware

  1. Слой авторизации всегда до маршрута: middleware, проверяющий права, должен располагаться перед функцией рендера страницы.
  2. Разделение middleware по функциям: одна функция для авторизации, другая — для проверки роли, третья — для логики перенаправления.
  3. Использование контекста ctx.state: хранение данных пользователя и состояния доступа позволяет легко обмениваться информацией между middleware.
  4. Обработка асинхронности корректно: для API-запросов всегда использовать промисы или async/await, чтобы избежать преждевременного рендеринга страницы.

Примеры комбинаций middleware

// Глобальное состояние пользователя
page('*', async (ctx, next) => {
    ctx.state.user = await fetchCurrentUser();
    next();
});

// Маршрут с авторизацией и проверкой роли
page('/settings', requireAuth, requireRole('editor'), () => {
    renderSettings();
});

// Маршрут доступен всем
page('/about', () => {
    renderAbout();
});

Эта структура позволяет масштабировать SPA, добавлять новые маршруты и права без нарушения существующей логики доступа.