diff --git a/owner-vue/src/api/auth.js b/owner-vue/src/api/auth.js new file mode 100644 index 0000000..32b2ef9 --- /dev/null +++ b/owner-vue/src/api/auth.js @@ -0,0 +1,26 @@ +import axios from "axios"; +import jwt from "@/common/jwt"; + +export default { + async requestReissue() { + const config = { + headers: { + "X-AUTH-TOKEN": jwt.getToken() + } + } + + const res = await axios.get("http://localhost:8001/user-service/auth/reissue", config); + + const accessToken = res.data.data.accessToken; + jwt.saveToken(accessToken); + jwt.saveExpiredTime(res.data.data.expiredTime); + axios.defaults.headers.common['Authorization'] = "Bearer " + accessToken; + + return accessToken; + }, + requestCheckAccessToken() { + axios.defaults.headers.common['Authorization'] = "Bearer " + jwt.getToken(); + + return axios.get("http://localhost:8001/user-service/auth/check/accessToken"); + } +} \ No newline at end of file diff --git a/owner-vue/src/api/user.js b/owner-vue/src/api/user.js index 0e1fff5..9a694ee 100644 --- a/owner-vue/src/api/user.js +++ b/owner-vue/src/api/user.js @@ -1,3 +1,5 @@ +import jwt from '../common/jwt.js'; + export default { requestRegisterUser(user) { return axios.post("http://localhost:8001/user-service/store-owner", user); @@ -11,10 +13,12 @@ export default { try { const response = await axios.post("http://localhost:8001/user-service/login", user); - console.log(response); - const AUTH_TOKEN = response.data.data.access_token; - localStorage.setItem('access_token', AUTH_TOKEN); - axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; + const data = response.data.data; + + jwt.saveToken(data.accessToken); + jwt.saveExpiredTime(data.expiredTime); + + axios.defaults.headers.common['Authorization'] = "Bearer " + data.accessToken; return true; } catch (err) { diff --git a/owner-vue/src/common/jwt.js b/owner-vue/src/common/jwt.js new file mode 100644 index 0000000..0783eb5 --- /dev/null +++ b/owner-vue/src/common/jwt.js @@ -0,0 +1,38 @@ + +const moment = require('moment'); +const ACCESS_TOKEN_NAME = "accessToken"; +const EXPIRED_TIME_NAME = "expiredTime"; + +const tag = "[jwt]"; + +export default { + getToken() { + return localStorage.getItem(ACCESS_TOKEN_NAME); + }, + saveToken(token) { + localStorage.setItem(ACCESS_TOKEN_NAME, token); + }, + getExpiredTime() { + return localStorage.getItem(EXPIRED_TIME_NAME); + }, + saveExpiredTime(expiredTime) { + localStorage.setItem(EXPIRED_TIME_NAME, expiredTime); + }, + destroyAll() { + localStorage.removeItem(ACCESS_TOKEN_NAME); + localStorage.removeItem(EXPIRED_TIME_NAME); + }, + isExpired() { + const expiredTime = this.getExpiredTime(); + + const expiredMoment = moment(expiredTime); + let currentMoment = moment(); + + const difference = moment.duration(expiredMoment.diff(currentMoment)).asSeconds(); + + console.log(tag, "expireMoment = ", expiredMoment, "currentMoment = ", currentMoment, "diff = ", difference); + + // 만료 30초 전일 경우 만료로 판단 + return difference <= 30; + } +} \ No newline at end of file diff --git a/owner-vue/src/main.js b/owner-vue/src/main.js index c2d00de..d630ed4 100644 --- a/owner-vue/src/main.js +++ b/owner-vue/src/main.js @@ -1,7 +1,11 @@ -import Vue from 'vue' -import App from './App.vue' -import vuetify from './plugins/vuetify' -import router from './router' +import Vue from 'vue'; +import App from './App.vue'; +import vuetify from './plugins/vuetify'; +import router from './router'; +import axios from "axios"; +import auth from "./api/auth.js" + +axios.defaults.withCredentials = true; Vue.config.productionTip = false @@ -9,4 +13,33 @@ new Vue({ vuetify, router, render: h => h(App) -}).$mount('#app') \ No newline at end of file +}).$mount('#app') + +axios.interceptors.response.use( + (response) => { + return response; + }, + async (error) => { + try { + const originalRequest = error.config; + if (error.response.status === 401) { + // access token 만료 시 + if (error.response.data.code == "EXPIRED") { + const accessToken = await auth.requestReissue(); + + originalRequest.headers.Authorization = "Bearer " + accessToken; + return axios(originalRequest); + } + // 그외 에러일 시 + alert("로그인 정보가 일치하지 않습니다."); + await router.replace('/login'); + return; + } + } catch (error) { + alert("로그인 정보가 일치하지 않습니다."); + await router.replace('/login'); + return; + } + return Promise.reject(error); + } +); \ No newline at end of file diff --git a/owner-vue/src/router/index.js b/owner-vue/src/router/index.js index 1fc109b..5d593a4 100644 --- a/owner-vue/src/router/index.js +++ b/owner-vue/src/router/index.js @@ -4,13 +4,30 @@ import VueRouter from 'vue-router' import DashboardLayout from "@/views/Layout/DashboardLayout"; import AuthLayout from "@/views/Layout/AuthLayout"; +import jwt from "../common/jwt.js"; +import auth from "../api/auth.js"; + Vue.use(VueRouter) +const authCheck = async function (to, from, next) { + try { + if (jwt.isExpired()) { + // refresh 호출 + await auth.requestReissue(); + } else { + await auth.requestCheckAccessToken(); + } + } catch (error) { + await router.replace("/login"); + } + next(); +}; const routes = [ { path: '/dashboard', redirect: 'dashboard', component: DashboardLayout, + beforeEnter: authCheck, children: [ { path: "/dashboard", @@ -64,4 +81,4 @@ const router = new VueRouter({ routes }) -export default router +export default router \ No newline at end of file