From a4aab21b981f54d86bc93b4eb81ce7b7af26a77a Mon Sep 17 00:00:00 2001 From: Cindy Chang Date: Fri, 28 Jun 2024 12:10:19 +0800 Subject: [PATCH] feature: refresh token. if not logged in then refresh token; else redirect to login page. --- src/router/index.js | 2 +- src/stores/login.js | 46 ++++++++++++----------- src/utils/cookieUtil.js | 38 ++++++++++--------- src/views/Login/{index.vue => Login.vue} | 0 src/views/MainContainer.vue | 48 ++++++++++++++++++------ 5 files changed, 84 insertions(+), 50 deletions(-) rename src/views/Login/{index.vue => Login.vue} (100%) diff --git a/src/router/index.js b/src/router/index.js index 6d40897..deed596 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,7 +1,7 @@ import { createRouter, createWebHistory, } from "vue-router"; import AuthContainer from '@/views/AuthContainer.vue'; import MainContainer from '@/views/MainContainer.vue'; -import Login from '@/views/Login/index.vue'; +import Login from '@/views/Login/Login.vue'; import Files from '@/views/Files/index.vue'; import Upload from '@/views/Upload/index.vue'; import Map from '@/views/Discover/Map/index.vue'; diff --git a/src/stores/login.js b/src/stores/login.js index 674c635..a60f201 100644 --- a/src/stores/login.js +++ b/src/stores/login.js @@ -1,7 +1,7 @@ import { defineStore } from "pinia"; import axios from 'axios'; import apiError from '@/module/apiError.js'; -import { deleteCookie, setCookie } from "../utils/cookieUtil"; +import { deleteCookie, setCookie, getCookie } from "../utils/cookieUtil"; export default defineStore('loginStore', { // data, methods, computed @@ -11,13 +11,13 @@ export default defineStore('loginStore', { grant_type: 'password', // password | refresh_token username: '', password: '', - refresh_token: '' + refresh_token: undefined, }, isInvalid: false, userData: {}, isLoggedIn: false, rememberedReturnToUrl: "", - // expired: new Date().setMonth(6), // 設定 Refresh Token 的到期日為半年後 + expired: new Date().setMonth(6), // 設定 Refresh Token 的到期日為半年後 }), actions: { /** @@ -35,10 +35,10 @@ export default defineStore('loginStore', { try { const response = await axios.post(api, this.auth, config); const accessToken = response.data.access_token; - const refreshToken = response.data.refresh_token; + const refresh_token = response.data.refresh_token; // 將 token 儲存在 cookie document.cookie = `luciaToken=${accessToken}`; - // document.cookie = `luciaRefreshToken=${refreshToken};expires=${new Date(this.expired)};`; + document.cookie = `luciaRefreshToken=${refresh_token};expires=${new Date(this.expired)};`; this.isLoggedIn = true; setCookie("isLuciaLoggedIn", "true"); @@ -57,27 +57,31 @@ export default defineStore('loginStore', { }; }, /** - * Refresh Token (暫時沒做) + * Refresh Token */ - async refreshTokenLogin() { + async refreshToken() { + console.log('TODO:TODO:', this.auth); const api = '/api/oauth/token'; - const refreshToken = document.cookie.replace(/(?:(?:^|.*;\s*)luciaRefreshToken\s*\=\s*([^;]*).*$)|^.*$/, "$1"); this.auth.grant_type = 'refresh_token'; - this.auth.refresh_token = refreshToken; + this.auth.refresh_token = getCookie("luciaRefreshToken"); - // try { - // const response = await axios.post(api, this.auth, config); - // const newAccessToken = response.data.access_token; - // const newRefreshToken = response.data.refresh_token; - - // document.cookie = `luciaToken=${newAccessToken}`; - // document.cookie = `luciaRefreshToken=${newRefreshToken};expires=${this.expired}`; - - // defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`; - // } catch(error) { - // this.$router.push('/login'); - // } + try { + const response = await axios.post(api, this.auth, config); + console.log('response', response); + if(response.status === 200) { + const newAccessToken = response.data.access_token; + const newRefreshToken = response.data.refresh_token; + + document.cookie = `luciaToken=${newAccessToken}`; + document.cookie = `luciaRefreshToken=${newRefreshToken};expires=${this.expired}`; + + defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`; + } + } catch(error) { + // 若refresh token 失敗則導向至登入頁面 + this.$router.push('/login'); + } }, /** * Logout, tooken expired diff --git a/src/utils/cookieUtil.js b/src/utils/cookieUtil.js index c13ca71..5be2e8b 100644 --- a/src/utils/cookieUtil.js +++ b/src/utils/cookieUtil.js @@ -1,29 +1,33 @@ export function getCookie(name) { - const nameEQ = name + "="; - const ca = document.cookie.split(';'); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; + const nameEqual = name + "="; + const cookieArr = document.cookie.split(';'); + for (let i = 0; i < cookieArr.length; i++) { + let c = cookieArr[i]; while (c.charAt(0) === ' ') { c = c.substring(1, c.length); } - if (c.indexOf(nameEQ) === 0) { - return c.substring(nameEQ.length, c.length); + if (c.indexOf(nameEqual) === 0) { + return c.substring(nameEqual.length, c.length); } } return null; } - export function setCookie(name, value, days=1) { - let expires = ""; - if (days) { - const date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toUTCString(); - } - document.cookie = name + "=" + (value || "") + expires + "; path=/"; +export function setCookie(name, value, days=1) { + let expires = ""; + if (days) { + const date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); } + document.cookie = name + "=" + (value || "") + expires + "; path=/"; +} - export function deleteCookie(name, path = '/') { - document.cookie = name + '=; Max-Age=-99999999; path=' + path; - } +export function setCookieWithoutExpiration(name, value) { + document.cookie = name + "=" + (value || ""); +} + +export function deleteCookie(name, path = '/') { + document.cookie = name + '=; Max-Age=-99999999; path=' + path; +} \ No newline at end of file diff --git a/src/views/Login/index.vue b/src/views/Login/Login.vue similarity index 100% rename from src/views/Login/index.vue rename to src/views/Login/Login.vue diff --git a/src/views/MainContainer.vue b/src/views/MainContainer.vue index fe30e28..bd48115 100644 --- a/src/views/MainContainer.vue +++ b/src/views/MainContainer.vue @@ -33,9 +33,11 @@ export default { const allMapDataStore = AllMapDataStore(); const conformanceStore = ConformanceStore(); const pageAdminStore = PageAdminStore(); + const loginStore = LoginStore(); const { tempFilterId, createFilterId, temporaryData, postRuleData, ruleData } = storeToRefs(allMapDataStore); const { conformanceLogTempCheckId, conformanceFilterTempCheckId } = storeToRefs(conformanceStore); const router = useRouter(); + const { isLoggedIn, auth } = storeToRefs(loginStore); const setHighlightedNavItemOnLanding = () => { const currentPath = router.currentRoute.value.path; @@ -72,6 +74,7 @@ export default { ]), ...mapState(LoginStore, [ 'isLoggedIn', + 'auth', ]) }, methods: { @@ -81,23 +84,46 @@ export default { 'clearShouldKeepPreviousPageBoolean', 'setActivePageComputedByRoute', ],), + ...mapActions(LoginStore, [ + 'refreshToken', + ],), }, created() { // Save token in Headers. const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*\=\s*([^;]*).*$)|^.*$/, "$1"); this.$http.defaults.headers.common['Authorization'] = `Bearer ${token}`; }, - beforeRouteEnter(to, from, next) { - // 重新整理畫面以及第一次進入網頁時,beforeRouteEnter這個hook會被執行,然而beforeRouteUpdate不會被執行 - if (!getCookie("isLuciaLoggedIn")) { - next({ - path: '/login', - query: { - 'return-to': btoa(window.location.href), - } - }); - } else { - next(); + // 重新整理畫面以及第一次進入網頁時,beforeRouteEnter這個hook會被執行,然而beforeRouteUpdate不會被執行 + // PSEUDOCODE + // if (not logged in) { + // if (has refresh token) { + // refresh_token(); + // if (refresh failed) { + // go to log in(); + // } else { + // cookie add("refresh_token=" + refresh_token "; expire=****") + // } + // } else { + // go to log in(); + // } + // } + beforeRouteEnter(to, from, next) { + const loginStore = LoginStore(); + + if (!loginStore.isLoggedIn) { + if (getCookie('luciaRefreshToken')) { + loginStore.refreshToken(); + } else { + next({ + path: '/login', + query: { + // 記憶未來登入後要進入的網址,且記憶的時候要用base64編碼包裹住 + 'return-to': btoa(window.location.href), + } + }); + } + } else { + next(); } }, // Remember, Swal modal handling is called before beforeRouteUpdate