Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Аутентификация: обзор

Аутентификация — проверка того, кто пользователь: он вводит email и пароль, сервер их сверяет и запоминает вход. Запоминание называется сессией: сервер кладёт в зашифрованную cookie данные пользователя, и при следующих запросах знает, кто пришёл. В Nuxt готовую сессию даёт модуль nuxt-auth-utils. Урок — обзор: полноценная система с регистрацией и базой пользователей строится на тех же функциях.

Установка

npx nuxi module add auth-utils

Модулю нужен ключ шифрования cookie длиной не меньше 32 символов в переменной NUXT_SESSION_PASSWORD. При разработке модуль сам допишет его в файл .env; на рабочем сервере задайте его вручную.

Вход на сервере

Для примера в блоге один автор, а его пароль хранится в настройке adminPassword из урока о переменных окружения. Создайте server/api/login.post.ts:

// server/api/login.post.ts
export default defineEventHandler(async (event) => {
  const { email, password } = await readBody<{ email?: string; password?: string }>(event)
  const config = useRuntimeConfig(event)
 
  if (email !== 'anna@example.com' || password !== config.adminPassword) {
    throw createError({ statusCode: 401, message: 'Неверный email или пароль' })
  }
 
  await setUserSession(event, { user: { name: 'Анна', email } })
  return { ok: true }
})

setUserSession записывает пользователя в сессию. В настоящем проекте пользователи хранятся в базе, а пароли — в виде хеша; для этого модуль даёт серверные функции hashPassword и verifyPassword.

Страница входа

<!-- app/pages/login.vue -->
<script setup lang="ts">
const { fetch: refreshSession } = useUserSession()
const form = reactive({ email: '', password: '' })
 
async function login() {
  await $fetch('/api/login', { method: 'POST', body: form })
  await refreshSession()
  await navigateTo('/admin')
}
</script>
 
<template>
  <form @submit.prevent="login">
    <input v-model="form.email" type="email" placeholder="Email">
    <input v-model="form.password" type="password" placeholder="Пароль">
    <button>Войти</button>
  </form>
</template>

useUserSession() хранит сессию на стороне страницы. После успешного входа refreshSession перечитывает её, и пользователь попадает в /admin.

Кто вошёл

<!-- app/layouts/default.vue -->
<script setup lang="ts">
const { loggedIn, user, clear } = useUserSession()
</script>
 
<template>
  <header>
    <template v-if="loggedIn">
      <span>{{ user?.name }}</span>
      <button @click="clear">Выйти</button>
    </template>
    <NuxtLink v-else to="/login">Войти</NuxtLink>
  </header>
</template>

После входа в шапке видно имя «Анна» и кнопку «Выйти»; clear удаляет сессию.

Защита страниц и API

Middleware app/middleware/auth.ts теперь проверяет сессию вместо cookie token:

// app/middleware/auth.ts
export default defineNuxtRouteMiddleware(() => {
  const { loggedIn } = useUserSession()
 
  if (!loggedIn.value) {
    return navigateTo('/login')
  }
})

Подключите его не только к app/pages/admin.vue, но и к форме app/pages/posts/new.vue: definePageMeta({ middleware: 'auth' }). А обработчик, который создаёт статьи, проверяет сессию сам:

// server/api/posts.post.ts
export default defineEventHandler(async (event) => {
  await requireUserSession(event)
  // дальше — прежний код создания статьи
})

requireUserSession отвечает ошибкой 401, если запрос пришёл без сессии.

Частая ошибка

Защитить только страницу /posts/new и забыть про API. Middleware не пускает гостя на страницу, но запрос POST /api/posts можно отправить напрямую, например командой curl. Проверяйте вход в каждом серверном обработчике, который меняет данные: защита на страницах — для удобства, настоящая — на сервере.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.