在本秘籍中,我们将使用以下工具在全栈 Nuxt 应用中设置身份验证:Nuxt Auth Utils它提供了方便的工具来管理客户端和服务器端的会话数据。
该模块使用安全的密封 Cookie 来存储会话数据,因此您无需设置数据库来存储会话数据。
使用 nuxt CLI 安装 nuxt-auth-utils 模块。
npx nuxt module add auth-utils
nuxt-auth-utils 作为依赖项,并将其添加到我们 nuxt.config.ts 的 modules 部分。由于 nuxt-auth-utils 使用密封 Cookie 存储会话数据,因此会话 Cookie 使用 NUXT_SESSION_PASSWORD 环境变量中的密钥进行加密。
.env 中。NUXT_SESSION_PASSWORD=a-random-password-with-at-least-32-characters
在本秘籍中,我们将创建一个简单的 API 路由,根据静态数据登录用户。
让我们创建一个 /api/login API 路由,它将接受一个 POST 请求,并在请求体中包含电子邮件和密码。
import { z } from 'zod'
const bodySchema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
export default defineEventHandler(async (event) => {
const { email, password } = await readValidatedBody(event, bodySchema.parse)
if (email === '[email protected]' && password === 'iamtheadmin') {
// set the user session in the cookie
// this server util is auto-imported by the auth-utils module
await setUserSession(event, {
user: {
name: 'John Doe',
},
})
return {}
}
throw createError({
statusCode: 401,
message: 'Bad credentials',
})
})
zod 依赖项(npm i zod)。该模块公开了一个 Vue 可组合项,用于了解用户是否在我们的应用程序中进行身份验证
<script setup>
const { loggedIn, session, user, clear, fetch } = useUserSession()
</script>
让我们创建一个登录页面,其中包含一个表单,用于将登录数据提交到我们的 /api/login 路由。
<script setup lang="ts">
const { loggedIn, user, fetch: refreshSession } = useUserSession()
const credentials = reactive({
email: '',
password: '',
})
async function login () {
try {
await $fetch('/api/login', {
method: 'POST',
body: credentials,
})
// Refresh the session on client-side and redirect to the home page
await refreshSession()
await navigateTo('/')
} catch {
alert('Bad credentials')
}
}
</script>
<template>
<form @submit.prevent="login">
<input
v-model="credentials.email"
type="email"
placeholder="Email"
>
<input
v-model="credentials.password"
type="password"
placeholder="Password"
>
<button type="submit">
Login
</button>
</form>
</template>
保护服务器路由是确保数据安全的关键。客户端中间件对用户很有帮助,但如果没有服务器端保护,您的数据仍然可以被访问。保护任何包含敏感数据的路由至关重要,如果用户未登录,我们应该返回 401 错误。
auth-utils 模块提供了 requireUserSession 实用函数,以帮助确保用户已登录并具有活动会话。
让我们创建一个 /api/user/stats 路由的示例,只有经过身份验证的用户才能访问。
export default defineEventHandler(async (event) => {
// make sure the user is logged in
// This will throw a 401 error if the request doesn't come from a valid user session
const { user } = await requireUserSession(event)
// TODO: Fetch some stats based on the user
return {}
})
有了服务器端路由,我们的数据是安全的,但如果什么都不做,未经身份验证的用户在尝试访问 /users 页面时可能会得到一些奇怪的数据。我们应该创建一个客户端中间件以在客户端保护路由并将用户重定向到登录页面。
nuxt-auth-utils 提供了一个方便的 useUserSession 可组合项,我们将用它来检查用户是否已登录,如果未登录则将其重定向。
我们将在 /middleware 目录中创建一个中间件。与服务器不同,客户端中间件不会自动应用于所有端点,我们需要指定要应用它的位置。
export default defineNuxtRouteMiddleware(() => {
const { loggedIn } = useUserSession()
// redirect the user to the login screen if they're not authenticated
if (!loggedIn.value) {
return navigateTo('/login')
}
})
现在我们有了应用程序中间件来保护我们的路由,我们可以在显示经过身份验证的用户信息的主页上使用它。如果用户未经过身份验证,他们将被重定向到登录页面。
我们将使用 definePageMeta 将中间件应用于我们要保护的路由。
<script setup lang="ts">
definePageMeta({
middleware: ['authenticated'],
})
const { user, clear: clearSession } = useUserSession()
async function logout () {
await clearSession()
await navigateTo('/login')
}
</script>
<template>
<div>
<h1>Welcome {{ user.name }}</h1>
<button @click="logout">
Logout
</button>
</div>
</template>
我们还添加了一个注销按钮,以清除会话并将用户重定向到登录页面。
我们已成功在 Nuxt 应用程序中设置了非常基本的身份验证和会话管理。我们还保护了服务器端和客户端上的敏感路由,以确保只有经过身份验证的用户才能访问它们。
接下来,您可以
查看开源atidone 存储库以获取一个带有 OAuth 身份验证、数据库和 CRUD 操作的 Nuxt 应用程序的完整示例。