AdSense

網頁

2026/1/21

Vue Nuxt 3 使用Page Meta管理頁面角色存取

本篇介紹Vue Nuxt 3使用Page Meta來管理頁面的角色存取。


目前專案的路由角色權限控管,例如/home/member,是寫死在Middleware middleware/auth.global.ts中,但若頁面一多,繁雜的路由與角色存取對應程式碼會很難管理,所以下面把可存取路由的角色權限定義在各頁面的Page Meta,之後控制頁面的存取就不需要去更動Middleware。要再次強調的是,光靠前端頁面存取控制並無任何安全性,真正的把關還是要後端API進行控管。


事前要求

參考「Vue Nuxt 3 側邊選單權限控管」,了解角色權限控制。


定義Page Meta

在頁面中使用definePageMeta定義自訂屬性requireAuthrolesrequireAuth值為boolean,在Middleware用來判斷路由到此頁面的使用者是否需要驗證。roles為字串陣列,用來設定可存取此頁面的角色。

[會員管理]頁面需要驗證,且使用者必須有[admin]角色。

pages/home/member.vue

definePageMeta({
  requireAuth: true,
  roles: ['admin']
})

[出口業務]頁面需要驗證,使用者必須是[admin]或[user]角色。

pages/home/export/index.vue

definePageMeta({
  requireAuth: true,
  roles: ['admin', 'user']
})

[訂單明細]頁面需要驗證,使用者必須是[admin]或[user]角色。

pages/home/export/[orderId].vue

definePageMeta({
  middleware: 'check-order-id',
  requireAuth: true,
  roles: ['admin', 'user']
})

其他頁面沒有定義requireAuth則為false,所以會直接通過Middleware中的檢核。


修改Middleware

調整middleware/auth.global.ts,從to.meta取得Page Meta參數requireAuth判斷該頁面是否需要驗證;修改原本檢查路由與使用者角色的檢核部分,改從參數roles取得頁面設定的可存取角色

middleware/auth.global.ts

import { verifyUser } from "~/server/utils/auth";

export default defineNuxtRouteMiddleware(async (to) => {
  if (to.path === "/login") return; // 登入頁面不用驗證

  // 頁面definePageMeta沒定義requireAuth: true不用驗證
  if (!to.meta.requireAuth) return;

  const isLogin = useAuth();
  const userState = useUser();

  // 檢查SSR路由登入驗證
  if (import.meta.server) {
    const token = useCookie("token").value;
    if (!token) {
      return navigateTo("/login");
    }

    // 同步前端登入狀態
    isLogin.value = true;
    userState.value = await verifyUser(token);
  }

  // 檢查SPA路由登入驗證
  if (import.meta.client) {
    if (!isLogin.value) {
      return navigateTo("/login");
    }
  }

  // 檢查路由頁面可存取角色
  if (to.meta.roles && userState.value) {
    const allowedRoles = to.meta.roles as string[];

    if (!allowedRoles.includes(userState.value?.role)) {
      return navigateTo("/home");
    }
  }
});


導覽列使用者名稱

經過以上調整後,未定義requireAuth的頁面不用登入就可在瀏覽器網誌列輸入直接進入,例如/home/about,未登入的使用者在導覽列顯示使用者名稱為guest及[登入]按鈕,所以修改components/Header.vue如下。

components/Header.vue

<template>
  <header class="header">
    <!--...-->
    <!-- 右上角使用者資訊 -->
    <div class="nav-right" v-if="user">
      {{ user.name }} ({{ user.role }})
      <button @click="logout">登出</button>
    </div>
    <div v-else>guest
      <button @click="login">登入</button>
    </div>
  </header>
</template>

<script setup lang="ts">
const user = useUser()
const isLogin = useAuth()

const logout = async () => {
  await $fetch('/api/auth/logout', { method: 'POST' })
  isLogin.value = false
  user.value = null
  navigateTo('/login')
}

const login = () => {
  navigateTo('/login')
}
</script>

<!--...-->

github



測試

在瀏覽器網址輸入http://localhost:3000/about效果如下。



使用Page Meta管理角色存取的好處是[規則]跟[邏輯]分離:存取規則放在頁面本身,邏輯判斷放在Middleware,這樣提高了可維護性,程式碼也更容易擴充。

沒有留言:

AdSense