Яка правильна структура авторизації на фронтенді з Vue 3?

Авторизація на фронтенді з використанням Vue 3 може бути організована через керування станом користувача та взаємодію з сервером (зазвичай через API). Ось базова структура авторизації для Vue 3:

1. Налаштування маршрутизації (Vue Router)

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

import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from './stores/auth'

const routes = [
  {
    path: '/login',
    name: 'Login',
    component: () => import('./views/Login.vue')
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('./views/Dashboard.vue'),
    meta: { requiresAuth: true } // приватний маршрут
  }
]

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

// Глобальний захист маршрутів
router.beforeEach((to, from, next) => {
  const authStore = useAuthStore()
  
  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    next({ name: 'Login' }) // перенаправляє на сторінку входу
  } else {
    next()
  }
})

export default router

2. Менеджмент стану користувача (Pinia)

Замість Vuex, тепер використовується Pinia для керування станом авторизації.

Створення стору авторизації:

// stores/auth.js
import { defineStore } from 'pinia'
import axios from 'axios'

export const useAuthStore = defineStore('auth', {
  state: () => ({
    token: localStorage.getItem('token') || '',
    user: null
  }),
  actions: {
    async login(credentials) {
      const response = await axios.post('/api/login', credentials)
      const token = response.data.token
      localStorage.setItem('token', token)
      this.token = token
      await this.fetchUser()
    },
    async fetchUser() {
      const response = await axios.get('/api/user', {
        headers: {
          Authorization: `Bearer ${this.token}`
        }
      })
      this.user = response.data
    },
    logout() {
      this.token = ''
      this.user = null
      localStorage.removeItem('token')
    }
  },
  getters: {
    isAuthenticated: (state) => !!state.token,
    user: (state) => state.user
  }
})

3. Захист запитів API (Axios інтерцептор)

Додаємо інтерцептор для автоматичного додавання токена до кожного запиту.

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

axios.interceptors.request.use((config) => {
  const authStore = useAuthStore()
  if (authStore.isAuthenticated) {
    config.headers.Authorization = `Bearer ${authStore.token}`
  }
  return config
}, (error) => {
  return Promise.reject(error)
})

4. Компонент входу (Login.vue)

Компонент для введення логіну та пароля:

<template>
  <div>
    <form @submit.prevent="login">
      <input v-model="email" type="email" placeholder="Email">
      <input v-model="password" type="password" placeholder="Password">
      <button type="submit">Login</button>
    </form>
  </div>
</template>

<script>
import { useAuthStore } from '../stores/auth'

export default {
  data() {
    return {
      email: '',
      password: ''
    }
  },
  setup() {
    const authStore = useAuthStore()

    const login = async () => {
      try {
        await authStore.login({ email: email.value, password: password.value })
        this.$router.push({ name: 'Dashboard' })
      } catch (error) {
        console.error('Login failed:', error)
      }
    }

    return {
      email,
      password,
      login
    }
  }
}
</script>

5. Вихід та обробка помилок

Для обробки помилок автентифікації та виходу з системи використовуйте дії logout та додатково обробляйте помилки у запитах.

Висновок

  1. Pinia для менеджменту стану користувача.
  2. Маршрутизація захищена, перевіряє доступ до сторінок.
  3. Axios інтерцептор для додавання токена в заголовки запитів.
  4. Компоненти входу використовують Pinia для управління логікою входу.
  5. Вихід та обробка помилок через Pinia.

Ця структура забезпечує гнучкість і безпеку під час роботи з авторизацією на фронтенді з Vue 3 та Pinia.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *