會話和認證
介紹
在本食譜中,我們將使用以下方式在全棧 Nuxt 應用程式中設定認證:Nuxt Auth Utils它提供了管理客戶端和伺服器端會話資料的便捷工具。
該模組使用安全且密封的 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=a-random-password-with-at-least-32-characters
登入 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 === '[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>
保護 API 路由
保護伺服器路由是確保資料安全的關鍵。客戶端中介軟體對使用者有幫助,但如果沒有伺服器端保護,您的資料仍然可以被訪問。保護任何包含敏感資料的路由至關重要,如果使用者未登入,我們應該返回 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 應用程式中設定了非常基本的使用者認證和會話管理。我們還保護了伺服器端和客戶端上的敏感路由,以確保只有經過身份驗證的使用者才能訪問它們。
接下來,您可以:
- 使用20+ 支援的 OAuth 提供商
- 新增資料庫來儲存使用者,請參閱Nitro SQL 資料庫或NuxtHub SQL 資料庫
- 使用電子郵件和密碼註冊使用者,透過密碼雜湊
- 新增對WebAuthn / Passkeys
檢視開源atidone 倉庫以獲取一個完整的 Nuxt 應用程式示例,包含 OAuth 認證、資料庫和 CRUD 操作。