本篇介紹使用Vue Nuxt 3的中間件(Middleware)來做登入驗證。
一般網站都需要登入後才能使用裡面的功能,所以這裡加入登入頁給使用者進行登入。使用者輸入帳號密碼後,前端會向後端呼叫API進行驗證,通過驗證則允許路由至功能頁面,反之則返回登入頁。而這裡中間件的作用則是SSR路由(也就是直接在網址列輸入路由網址)時的驗證。
┌───────────┐ ┌────────────┐
│ SSR Route ├──────►│ middleware ├────┬───► /
└───────────┘ └─────┬──────┘ ├───► /home
│ ├───► /home/export
/login └───► /about
│
▼
┌───────────┐
│ login.vue ├
└───────────┘
事前要求
參考「Vue Nuxt 3 使用路由中間件(Middleware)」。
建立登入API
新增server/api/auth/login.post.ts,Nuxt會將server/api目錄中的檔案自動轉換為API端點,即後端API [POST] /api/auth/login,用來模擬驗證使用者帳號密碼,通過驗證則將token存在Cookies並回傳。
server/api/auth/login.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const { username, password } = body;
// 模擬驗證並返回token
if (username === "admin" && password === "1234") {
const token = "fake-jwt-token";
setCookie(event, "token", token, {
httpOnly: true,
maxAge: 60 * 60 * 24, // 1 天
});
return {
success: true,
token: token,
};
}
return {
success: false,
};
});
建立回應介面
新增types目錄,並在裡面建立auth.ts,內容定義一個LoginResponse介面來描述登入API的回應結構。token?: string的意思相當於token: string | undefined,也就是可接受string或undefined型別。
types/auth.ts
export interface LoginResponse {
success: boolean
token?: string
}
建立登入頁layout
新增layouts/auth.vue,為登入頁login.vue使用的layout。
<template>
<div>
<NuxtPage />
</div>
</template>
建立登入頁
新增pages/login.vue為登入頁面,點擊[登入]按鈕會呼叫後端API [POST] /api/auth/login並傳入帳號密碼進行驗證,若成功則導向至/home。
pages/login.vue
<template>
<h1>登入</h1>
<form @submit.prevent="login">
<label>
帳號:
<input v-model="username" placeholder="帳號" />
</label>
<br>
<label>
密碼:
<input v-model="password" type="password" placeholder="密碼" />
</label>
<br>
<button type="submit">登入</button>
</form>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'auth'
})
import { ref } from 'vue'
import type { LoginResponse } from '~/types/auth'
const username = ref('')
const password = ref('')
const login = async () => {
try {
const { success, token } = await $fetch<LoginResponse>('/api/auth/login', {
method: 'POST',
body: { username: username.value, password: password.value }
})
if (success && token) {
navigateTo('/home')
}
} catch (err) {
alert('登入失敗,請稍後再試')
}
}
</script>
建立Middleware
新增middleware/auth.global.ts,檔案名稱後接.global為全域中間件,即會自動套用在每個路由。
此中間件用來檢查除了/login外的其他SSR路由時是否有token,若沒有代表未曾登入過,導回登入頁面。if (import.meta.server)的意思是只在SSR路由時做檢查,前端SPA路由時則不會檢查token,因為httpOnly cookie只能在server端讀取,前端無法存取,因此SPA路由的驗證要透過登入狀態來實現。
middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to) => {
if (to.path === "/login") return; // 登入頁面不用驗證
if (import.meta.server) {
const token = useCookie("token").value;
if (!token) {
return navigateTo("/login");
}
}
});
測試
在瀏覽器輸入http://localhost:3000/home(即SSR路由),若未登入會回到[登入]頁面,而不會導向[Home]頁面。
開啟瀏覽器開發者模式刪除此網站的token Cookie即可登出。
沒有留言:
張貼留言