Аутентификация: обзор
Аутентификация — проверка того, кто пользователь: он вводит 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. Проверяйте вход в каждом серверном обработчике, который меняет данные: защита на страницах — для удобства, настоящая — на сервере.