AdSense

網頁

2026/1/15

Vue Nuxt 3 登入驗證

本篇介紹使用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,也就是可接受stringundefined型別。

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");
    }
  }
});

github



測試

在瀏覽器輸入http://localhost:3000/home(即SSR路由),若未登入會回到[登入]頁面,而不會導向[Home]頁面。

開啟瀏覽器開發者模式刪除此網站的token Cookie即可登出。

沒有留言:

AdSense