From 9ad4313e43b17e15df72a9e576ea7d9d8ffffac3 Mon Sep 17 00:00:00 2001 From: Tony Yang Date: Wed, 16 Apr 2025 16:51:40 +0800 Subject: [PATCH] feat: auth route guard --- src/router/index.js | 29 ++++++++++++++++++++++++++--- src/stores/auth.js | 26 +++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/router/index.js b/src/router/index.js index 5611dd4..1aeda6f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -61,7 +61,8 @@ const routes = [ component: ProfileView, meta: { showInNav: false, - navName: '個人資料' + navName: '個人資料', + requiresAuth: true } }, { @@ -71,7 +72,8 @@ const routes = [ meta: { keepAlive: true, showInNav: true, - navName: '每日金句' + navName: '每日金句', + requiresAuth: true } } ]; @@ -84,8 +86,29 @@ const router = createRouter({ export function unauthRedirectToLogin() { const auth = useAuthStore(); auth.clearJwt(); - alert('Your session has expired. Redirecting to login.'); + alert('您的登入驗證已過期。將重新導向至登入頁面。'); router.push('/login'); } +router.beforeEach((to, from, next) => { + const auth = useAuthStore(); + if (auth.isLoggedIn && (to.path === '/login' || to.path === '/register')) { + next('/profile'); + } else if (to.meta.requiresAuth) { + if (!auth.isLoggedIn) { + auth.clearJwt(); + if (auth.isExpired) { + alert('您的登入驗證已過期。將重新導向至登入頁面...'); + } else { + alert('請登入以使用此服務。將重新導向至登入頁面...'); + } + next('/login'); + } else { + next(); + } + } else { + next(); + } +}); + export default router; diff --git a/src/stores/auth.js b/src/stores/auth.js index 08bae25..5d3bef5 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -1,11 +1,35 @@ import { defineStore } from 'pinia'; +function isJwtExpired(jwt) { + if (!jwt) return true; + + try { + const payload = JSON.parse(atob(jwt.split('.')[1])); + const expirationTime = payload.exp * 1000; // Convert to milliseconds + return Date.now() >= expirationTime; + } catch (error) { + console.error("Failed to decode JWT:", error); + return true; // Treat as expired if there's an error + } +} + export const useAuthStore = defineStore('auth', { state: () => ({ jwt: localStorage.getItem('jwt') || null, }), getters: { - isLoggedIn: (state) => !!state.jwt, + isLoggedIn: (state) => { + if (state.jwt) { + return !isJwtExpired(state.jwt); + } + return false; + }, + isExpired: (state) => { + if (state.jwt) { + return isJwtExpired(state.jwt); + } + return false; + }, id: (state) => { if (state.jwt) { try {