Библиотека Universal Router предоставляет гибкий механизм маршрутизации, который можно использовать не только для обработки URL и переходов, но и для реализации контроля доступа к отдельным маршрутам. Ограничение доступа — это процесс, при котором маршруты становятся доступными только для определённых пользователей или ролей, в зависимости от условий, задаваемых в конфигурации.
В Universal Router доступ контролируется через
свойства маршрута, функцию action и контекст
маршрутизатора. Основная идея заключается в проверке условий до
выполнения основной логики маршрута.
Контекст (context) передаётся в функцию
resolve маршрутизатора и содержит любую дополнительную
информацию о текущем состоянии приложения: данные пользователя, токены
аутентификации, роли и другие параметры.
Пример структуры контекста:
const context = {
user: {
isAuthenticated: true,
role: 'admin'
}
};
Каждый маршрут может содержать функцию action, которая
возвращает результат выполнения маршрута или бросает ошибку, если доступ
запрещён. В качестве ключевого механизма контроля доступа часто
используется проверка состояния пользователя в контексте:
import UniversalRouter from 'universal-router';
const routes = [
{
path: '/dashboard',
action: (context) => {
if (!context.user || !context.user.isAuthenticated) {
throw { status: 401, message: 'Доступ запрещён' };
}
return 'Добро пожаловать в панель управления';
}
},
{
path: '/admin',
action: (context) => {
if (context.user.role !== 'admin') {
throw { status: 403, message: 'Нет прав администратора' };
}
return 'Административная панель';
}
}
];
const router = new UniversalRouter(routes);
router.resolve({ pathname: '/admin', user: { role: 'user' } })
.catch(error => console.error(error.status, error.message));
В этом примере /dashboard доступен только
аутентифицированным пользователям, а /admin — только
пользователям с ролью admin.
Для крупных приложений удобно создавать отдельные функции-посредники, которые будут проверять права доступа до выполнения основной логики маршрута. Это позволяет избежать дублирования кода и централизовать управление безопасностью.
function requireAuth(context) {
if (!context.user || !context.user.isAuthenticated) {
throw { status: 401, message: 'Требуется аутентификация' };
}
}
function requireRole(role) {
return (context) => {
if (!context.user || context.user.role !== role) {
throw { status: 403, message: 'Недостаточно прав' };
}
};
}
const routes = [
{
path: '/profile',
action: (context) => {
requireAuth(context);
return 'Страница пользователя';
}
},
{
path: '/admin/settings',
action: (context) => {
requireAuth(context);
requireRole('admin')(context);
return 'Настройки администратора';
}
}
];
Такой подход делает маршруты более читаемыми и позволяет легко масштабировать правила доступа.
В реальных приложениях информация о пользователе может загружаться с
сервера или из хранилища, поэтому проверки часто выполняются асинхронно.
Universal Router поддерживает асинхронные функции
action:
const routes = [
{
path: '/premium',
async action(context) {
const user = await fetchUserData(context.token);
if (!user.isPremium) {
throw { status: 403, message: 'Только для премиум-пользователей' };
}
return 'Контент премиум-класса';
}
}
];
Асинхронные проверки позволяют интегрировать маршрутизацию с внешними API и сервисами авторизации, не блокируя основной поток выполнения.
Для корректного UX важно обрабатывать ошибки доступа и перенаправлять пользователя на соответствующие страницы:
router.resolve({ pathname: '/admin', user: { role: 'user' } })
.then(result => console.log(result))
.catch(error => {
if (error.status === 401) {
window.location.href = '/login';
} else if (error.status === 403) {
window.location.href = '/forbidden';
} else {
console.error(error);
}
});
Такой подход позволяет централизованно управлять сценариями недоступных маршрутов без изменения логики самих маршрутов.
Для сложных систем часто создаётся отдельная матрица прав доступа:
const rolesPermissions = {
user: ['/dashboard', '/profile'],
admin: ['/dashboard', '/profile', '/admin', '/admin/settings']
};
function checkAccess(context, path) {
const allowedPaths = rolesPermissions[context.user.role] || [];
if (!allowedPaths.includes(path)) {
throw { status: 403, message: 'Доступ к маршруту запрещён' };
}
}
const routes = [
{
path: '/admin/settings',
action: (context) => {
checkAccess(context, '/admin/settings');
return 'Страница настроек администратора';
}
}
];
Такой механизм позволяет централизованно управлять доступом по ролям и легко расширять систему при добавлении новых маршрутов или ролей.
Universal Router позволяет комбинировать все
перечисленные подходы: проверку в action, использование
guard-функций, асинхронные проверки и централизованную матрицу прав. Это
создаёт гибкую и безопасную систему маршрутизации с поддержкой любых
требований по доступу.
Применение этих техник обеспечивает строгий контроль над доступом к маршрутам и позволяет интегрировать Universal Router в современные веб-приложения с различными сценариями авторизации и аутентификации.