会话与认证
简介
本指南将使用Nuxt Auth Utils在全栈Nuxt应用中设置认证系统,该工具提供了便捷的客户端和服务端会话数据管理功能。
该模块使用安全密封的Cookie存储会话数据,因此您无需设置数据库来存储会话信息。
安装nuxt-auth-utils
使用nuxt
CLI安装nuxt-auth-utils
模块。
npx nuxt module add auth-utils
nuxt-auth-utils
作为依赖项,并将其添加到nuxt.config.ts
的modules
部分Cookie加密密钥
由于nuxt-auth-utils
使用密封Cookie存储会话数据,会话Cookie会使用NUXT_SESSION_PASSWORD
环境变量中的密钥进行加密。
.env
文件中。NUXT_SESSION_PASSWORD=至少32个字符的随机密码
登录API路由
本指南中,我们将基于静态数据创建一个简单的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 === 'admin@admin.com' && password === 'iamtheadmin') {
// 在Cookie中设置用户会话
// 此服务端工具由auth-utils模块自动导入
await setUserSession(event, {
user: {
name: 'John Doe'
}
})
return {}
}
throw createError({
statusCode: 401,
message: '凭证错误'
})
})
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() {
$fetch('/api/login', {
method: 'POST',
body: credentials
})
.then(async () => {
// 在客户端刷新会话并重定向到首页
await refreshSession()
await navigateTo('/')
})
.catch(() => alert('凭证错误'))
}
</script>
<template>
<form @submit.prevent="login">
<input v-model="credentials.email" type="email" placeholder="邮箱" />
<input v-model="credentials.password" type="password" placeholder="密码" />
<button type="submit">登录</button>
</form>
</template>
保护API路由
保护服务端路由是确保数据安全的关键。客户端中间件对用户很有帮助,但如果没有服务端保护,数据仍然可能被访问。保护任何包含敏感数据的路由至关重要,如果用户未登录这些路由,我们应该返回401错误。
auth-utils
模块提供了requireUserSession
工具函数来确保用户已登录并拥有有效会话。
创建一个只有认证用户可以访问的/api/user/stats
路由示例。
export default defineEventHandler(async (event) => {
// 确保用户已登录
// 如果请求不是来自有效用户会话,将抛出401错误
const { user } = await requireUserSession(event)
// TODO: 基于用户获取一些统计数据
return {}
});
保护应用路由
通过服务端路由我们的数据已经安全,但如果不做其他处理,未认证用户在尝试访问/users
页面时可能会看到奇怪的数据。我们应该创建一个客户端中间件来在客户端保护路由,并将用户重定向到登录页面。
nuxt-auth-utils
提供了方便的useUserSession
组合式函数,我们将用它来检查用户是否登录,如果未登录则重定向。
在/middleware
目录中创建一个中间件。与服务端不同,客户端中间件不会自动应用到所有端点,我们需要指定应用位置。
export default defineNuxtRouteMiddleware(() => {
const { loggedIn } = useUserSession()
// 如果用户未认证,则重定向到登录页面
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>欢迎 {{ user.name }}</h1>
<button @click="logout">退出登录</button>
</div>
</template>
我们还添加了一个退出登录按钮来清除会话并将用户重定向到登录页面。
总结
我们已成功在Nuxt应用中设置了非常基本的用户认证和会话管理。我们还保护了服务端和客户端的敏感路由,确保只有认证用户可以访问。
后续步骤,您可以:
- 使用20+支持的OAuth提供商添加认证
- 添加数据库存储用户,参见Nitro SQL数据库或NuxtHub SQL数据库
- 使用密码哈希让用户通过邮箱和密码注册
- 添加WebAuthn/Passkeys支持
查看开源atidone仓库获取包含OAuth认证、数据库和CRUD操作的完整Nuxt应用示例。