Хранение состояния аутентификации

При разработке современных одностраничных приложений (SPA) с использованием Vue.js одной из ключевых задач является корректное управление состоянием аутентификации пользователя. Vue Router предоставляет гибкие инструменты для реализации защищённых маршрутов и контроля доступа.

Использование навигационных охранников (Navigation Guards)

Навигационные охранники позволяют перехватывать переходы между маршрутами и выполнять проверки перед их выполнением. Для управления доступом чаще всего используется глобальный охранник beforeEach:

import { createRouter, createWebHistory } from 'vue-router';
import Home from './components/Home.vue';
import Dashboard from './components/Dashboard.vue';
import Login from './components/Login.vue';
import { isAuthenticated } from './auth'; // функция проверки аутентификации

const routes = [
  { path: '/', component: Home },
  { path: '/login', component: Login },
  { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !isAuthenticated()) {
    next('/login');
  } else {
    next();
  }
});

export default router;

Ключевой момент — использование свойства meta.requiresAuth. Это позволяет централизованно маркировать маршруты, которые требуют проверки состояния аутентификации.

Хранение токена и управление состоянием

Для проверки аутентификации обычно применяются токены, хранящиеся в localStorage, sessionStorage или Vuex/Pinia.

Пример использования Pinia для хранения токена:

import { defineStore } from 'pinia';

export const useAuthStore = defineStore('auth', {
  state: () => ({
    token: localStorage.getItem('token') || null,
  }),
  getters: {
    isAuthenticated: (state) => !!state.token,
  },
  actions: {
    login(token) {
      this.token = token;
      localStorage.setItem('token', token);
    },
    logout() {
      this.token = null;
      localStorage.removeItem('token');
    },
  },
});

Связка Pinia и Vue Router позволяет делать проверку состояния аутентификации внутри охранников:

import { useAuthStore } from './stores/auth';

router.beforeEach((to, from, next) => {
  const auth = useAuthStore();
  if (to.meta.requiresAuth && !auth.isAuthenticated) {
    next('/login');
  } else {
    next();
  }
});

Защита маршрутов на уровне компонентов

Иногда необходимо ограничить доступ к определённым компонентам, даже если пользователь смог попасть на страницу через обход URL. Для этого в компонентах можно использовать beforeRouteEnter и beforeRouteUpdate:

export default {
  beforeRouteEnter(to, from, next) {
    const auth = useAuthStore();
    if (!auth.isAuthenticated) {
      next('/login');
    } else {
      next();
    }
  },
  beforeRouteUpdate(to, from, next) {
    const auth = useAuthStore();
    if (!auth.isAuthenticated) {
      next('/login');
    } else {
      next();
    }
  },
};

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

Синхронизация состояния с локальным хранилищем

Для предотвращения сброса состояния при перезагрузке страницы важно синхронизировать Vuex или Pinia с localStorage:

import { watch } from 'vue';
import { useAuthStore } from './stores/auth';

const auth = useAuthStore();

watch(
  () => auth.token,
  (newToken) => {
    if (newToken) {
      localStorage.setItem('token', newToken);
    } else {
      localStorage.removeItem('token');
    }
  }
);

Это гарантирует, что пользователь останется авторизованным после обновления страницы.

Обработка истечения сессии

Для поддержания безопасности стоит отслеживать срок действия токена. Один из способов — проверка JWT:

import jwtDecode from 'jwt-decode';
import { useAuthStore } from './stores/auth';

export function isAuthenticated() {
  const auth = useAuthStore();
  if (!auth.token) return false;

  try {
    const { exp } = jwtDecode(auth.token);
    return Date.now() < exp * 1000;
  } catch {
    auth.logout();
    return false;
  }
}

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

Роль Vue Router в поддержке многоуровневого доступа

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

const routes = [
  { path: '/admin', component: Admin, meta: { requiresAuth: true, role: 'admin' } },
  { path: '/profile', component: Profile, meta: { requiresAuth: true, role: 'user' } },
];

router.beforeEach((to, from, next) => {
  const auth = useAuthStore();
  if (to.meta.requiresAuth) {
    if (!auth.isAuthenticated) return next('/login');
    if (to.meta.role && auth.role !== to.meta.role) return next('/');
  }
  next();
});

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

Итоговые рекомендации по хранению состояния

  • Токен хранится в Pinia/Vuex с синхронизацией в localStorage или sessionStorage.
  • Навигационные охранники beforeEach используются для глобальной проверки аутентификации.
  • beforeRouteEnter и beforeRouteUpdate защищают отдельные компоненты.
  • Проверка JWT или срока действия токена обеспечивает безопасность.
  • Свойство meta маршрута позволяет управлять доступом по ролям и уровням.

Такой подход обеспечивает надёжное управление состоянием аутентификации и интеграцию с маршрутизацией Vue Router, создавая безопасное и масштабируемое SPA.