vue2_frontend...

This commit is contained in:
최준흠 2022-09-15 17:34:10 +09:00
parent 22543d0f15
commit 8da393dd29
15 changed files with 215 additions and 148 deletions

View File

@ -80,13 +80,10 @@ export default {
getValidationState({ dirty, validated, valid = null }) { getValidationState({ dirty, validated, valid = null }) {
return dirty || validated ? valid : null return dirty || validated ? valid : null
}, },
onSubmit() { async onSubmit() {
try { try {
authService.login({ await authService.login(this.form.email, this.form.password)
email: this.form.email, await this.$router.push({
password: this.form.password
})
this.$router.push({
name: this.$route.params.return_url || 'home' name: this.$route.params.return_url || 'home'
}) })
} catch (e) { } catch (e) {

View File

@ -136,14 +136,14 @@
</div> </div>
</template> </template>
<script> <script>
import createNamespacedHelpers from 'vuex'
import todoService from '@/service/todo.service' import todoService from '@/service/todo.service'
import { createNamespacedHelpers } from 'vuex' const store = createNamespacedHelpers('TodoStore')
const todoStore = createNamespacedHelpers('TodoStore')
// : https://vuejsexamples.com/vuejs-tables-and-select-all-checkbox/ // : https://vuejsexamples.com/vuejs-tables-and-select-all-checkbox/
export default { export default {
components: {}, components: {},
computed: { computed: {
...todoStore.mapGetters(['getTotal', 'getRows']) ...store.mapGetters(['getTotal', 'getRows'])
}, },
created: function () { created: function () {
this.setDatas() this.setDatas()

View File

@ -1,45 +1,32 @@
import api from './auth.api' import authApi from './auth.api'
import jwt from '../service/jwt' import tokenService from '../service/token.service'
//참조: https://www.bezkoder.com/vue-refresh-token/ //참조: https://www.bezkoder.com/vue-refresh-token/
const setup = (store) => { const setup = (store) => {
//API Request 전 처리용
api.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded'
//Request 처리용 //Request 처리용
api.interceptors.request.use( authApi.interceptors.request.use(
(config) => jwt.getAuthorizationHeader(config), (config) => {
const token = tokenService.getLocalAccessToken()
if (token) {
// config.headers["Authorization"] = 'Bearer ' + token; // for Spring Boot back-end
config.headers['x-access-token'] = token // for Node.js Express back-end
}
return config
},
(error) => { (error) => {
// Do something with request error // Do something with request error
Promise.reject(error) Promise.reject(error)
} }
) )
//AccessToken이 만료된경우 다시 받기위한 함수
const reloadAccessToken = async (originalConfig) => {
// Access Token이 expired,오류로 인한 response 401 답볍을 받은경우
originalConfig._retry = true
try {
//Refresh Token으로 다시 Access Token 재생성 후 로그인 다시하라고 오류보냄
await api
.post('/auth/reload', {
refresh_token: jwt.getRefreshToken()
})
.then((rs) => {
store.dispatch('reload', rs.data.access_token)
})
return api(originalConfig)
} catch (_error) {
return Promise.reject(_error)
}
}
//Response 처리용 //Response 처리용
api.interceptors.response.use( authApi.interceptors.response.use(
(response) => { (response) => {
console.log('AUTH API Call 성공=>' + response)
return response return response
}, },
async (err) => { async (err) => {
// Do something with response error //err.config->이전에 보냈던 api (url등)정보 전달용
//err.response->오류 상태를 알기 위해
//originalConfig._retry는 처음 시도용인지 알기 위해
const originalConfig = err.config const originalConfig = err.config
if (originalConfig.url !== '/auth/login' && err.response) { if (originalConfig.url !== '/auth/login' && err.response) {
if (err.response.status === 401 && !originalConfig._retry) { if (err.response.status === 401 && !originalConfig._retry) {
@ -49,5 +36,27 @@ const setup = (store) => {
return Promise.reject(err) return Promise.reject(err)
} }
) )
//AccessToken이 만료된경우 다시 받기위한 함수
const reloadAccessToken = async (originalConfig) => {
// Access Token이 expired,오류로 인한 response 401 답볍을 받은경우
originalConfig._retry = true
try {
//Refresh Token으로 다시 Access Token 재생성 후 로그인 다시하라고 오류보냄
await authApi
.post('/auth/reload', {
refresh_token: tokenService.getLocalRefreshToken()
})
.then((rs) => {
const { access_token } = rs.data
store.dispatch('reload', access_token)
tokenService.updateLocalAccessToken(access_token)
})
//reload로 access_token을 재발급을 받았으면 이전 APICall 다시 호출
return await authApi(originalConfig)
} catch (_error) {
return Promise.reject(_error)
}
}
} }
export default setup export default setup

View File

@ -1,7 +1,5 @@
import axios from 'axios' import axios from 'axios'
const authAPI = axios.create({ const authApi = axios.create({
baseURL: process.env.VUE_APP_AUTH_HOST, baseURL: process.env.VUE_APP_AUTH_HOST
headers: { 'Content-type': 'application/json' }
}) })
export default authApi
export default authAPI

View File

@ -0,0 +1,70 @@
import axios from 'axios'
import authApi from './auth.api'
import tokenService from '../service/token.service'
//참조: https://www.bezkoder.com/vue-refresh-token/
const interceptor = () => {
//API Request 전 처리용
axios.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded'
axios.defaults.headers.get['Accepts'] = 'application/json'
axios.defaults.timeout = 2500
//Request 처리용
axios.interceptors.request.use(
(config) => {
const token = tokenService.getLocalAccessToken()
if (token) {
config.headers['Authorization'] = 'Bearer ' + token // for Spring Boot back-end
config.headers['x-access-token'] = token // for Node.js Express back-end
}
return config
},
(error) => {
// Do something with request error
Promise.reject(error)
}
)
//Response 처리용
axios.interceptors.response.use(
(response) => {
return response
},
async (err) => {
//err.config->이전에 보냈던 api (url등)정보 전달용
//err.response->오류 상태를 알기 위해
//originalConfig._retry는 처음 시도용인지 알기 위해
const originalConfig = err.config
if (originalConfig.url !== '/auth/login' && err.response) {
if (err.response.status === 401 && !originalConfig._retry) {
return await reloadAccessToken(originalConfig)
}
}
return Promise.reject(err)
}
)
//AccessToken이 만료된경우 다시 받기위한 함수
const reloadAccessToken = async (originalConfig) => {
// Access Token이 expired,오류로 인한 response 401 답볍을 받은경우
originalConfig._retry = true
try {
//Refresh Token으로 다시 Access Token 재생성 후 로그인 다시하라고 오류보냄
await authApi
.post('/auth/reload', {
refresh_token: tokenService.getLocalRefreshToken()
})
.then((rs) => {
const { access_token } = rs.data
tokenService.updateLocalAccessToken(access_token)
})
//reload로 access_token을 재발급을 받았으면 이전 APICall 다시 호출
return await axios(originalConfig)
} catch (_error) {
return Promise.reject(_error)
}
}
}
export default interceptor

View File

@ -1,58 +1,63 @@
import api from './auth.api' import todoApi from './todo.api'
import jwt from '../service/jwt' import authApi from './auth.api'
import tokenService from '../service/token.service'
//참조: https://www.bezkoder.com/vue-refresh-token/ //참조: https://www.bezkoder.com/vue-refresh-token/
const setup = (store) => { const setup = (store) => {
//API Request 전 처리용
api.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded'
//Request 처리용 //Request 처리용
api.interceptors.request.use( todoApi.interceptors.request.use(
(config) => jwt.getAuthorizationHeader(config), (config) => {
const token = tokenService.getLocalAccessToken()
if (token) {
// config.headers["Authorization"] = 'Bearer ' + token; // for Spring Boot back-end
config.headers['x-access-token'] = token // for Node.js Express back-end
}
return config
},
(error) => { (error) => {
// Do something with request error // Do something with request error
Promise.reject(error) Promise.reject(error)
} }
) )
//Response 처리용
todoApi.interceptors.response.use(
(response) => {
return response
},
async (err) => {
//err.config->이전에 보냈던 api (url등)정보 전달용
//err.response->오류 상태를 알기 위해
//originalConfig._retry는 처음 시도용인지 알기 위해
const originalConfig = err.config
if (originalConfig.url !== '/auth/login' && err.response) {
if (err.response.status === 401 && !originalConfig._retry) {
return await reloadAccessToken(originalConfig)
}
}
return Promise.reject(err)
}
)
//AccessToken이 만료된경우 다시 받기위한 함수 //AccessToken이 만료된경우 다시 받기위한 함수
const reloadAccessToken = async (originalConfig) => { const reloadAccessToken = async (originalConfig) => {
// Access Token이 expired,오류로 인한 response 401 답볍을 받은경우 // Access Token이 expired,오류로 인한 response 401 답볍을 받은경우
originalConfig._retry = true originalConfig._retry = true
try { try {
//Refresh Token으로 다시 Access Token 재생성 후 로그인 다시하라고 오류보냄 //Refresh Token으로 다시 Access Token 재생성 후 로그인 다시하라고 오류보냄
await api await authApi
.post('/auth/reload', { .post('/auth/reload', {
refresh_token: jwt.getRefreshToken() refresh_token: tokenService.getLocalRefreshToken()
}) })
.then((rs) => { .then((rs) => {
store.dispatch('reload', rs.data.access_token) const { access_token } = rs.data
store.dispatch('reload', access_token)
tokenService.updateLocalAccessToken(access_token)
}) })
return api(originalConfig) //reload로 access_token을 재발급을 받았으면 이전 APICall 다시 호출
return await authApi(originalConfig)
} catch (_error) { } catch (_error) {
return Promise.reject(_error) return Promise.reject(_error)
} }
} }
//Response 처리용
api.interceptors.response.use(
async (response) => {
switch (response.config.url) {
case '/todo':
await store.dispatch('setDatas', response.data)
break
}
return response
},
async (err) => {
// Do something with response error
const originalConfig = err.config
if (originalConfig.url !== '/auth/login' && err.response) {
if (err.response.status === 401 && !originalConfig._retry) {
return await reloadAccessToken(originalConfig)
}
}
console.log('TODO API Call 오류=>' + err)
return Promise.reject(err)
}
)
} }
export default setup export default setup

View File

@ -1,6 +1,5 @@
import axios from 'axios' import axios from 'axios'
const todoAPI = axios.create({ const todoApi = axios.create({
baseURL: process.env.VUE_APP_TODO_HOST, baseURL: process.env.VUE_APP_TODO_HOST
headers: { 'Content-type': 'application/json' }
}) })
export default todoAPI export default todoApi

View File

@ -27,6 +27,10 @@ localize('ko', ko)
Vue.component('ValidationObserver', ValidationObserver) Vue.component('ValidationObserver', ValidationObserver)
Vue.component('ValidationProvider', ValidationProvider) Vue.component('ValidationProvider', ValidationProvider)
//인터셉터 정의 및 Storage 연동
import interceptor from './interceptors/iterceptor'
interceptor()
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.use(BootstrapVue) Vue.use(BootstrapVue)
new Vue({ new Vue({

View File

@ -1,7 +1,7 @@
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue' import HomeView from '../views/HomeView.vue'
import jwt from '../service/jwt' import tokenService from '../service/token.service'
Vue.use(VueRouter) Vue.use(VueRouter)
const routes = [ const routes = [
@ -58,12 +58,13 @@ const router = new VueRouter({
// next : to에서 지정한 url로 이동하기 위해 꼭 호출해야 하는 함수 // next : to에서 지정한 url로 이동하기 위해 꼭 호출해야 하는 함수
//여기에서 login은 path가 아니라 name에서 찾는다 //여기에서 login은 path가 아니라 name에서 찾는다
//router.beforeEach()를 호출하고 나면 모든 라우팅이 대기 상태가 된다 //router.beforeEach()를 호출하고 나면 모든 라우팅이 대기 상태가 된다
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
//1. routes 설정에서 meta: { requiredAuth: true } 가 선언된 경우 //1. routes 설정에서 meta: { requiredAuth: true } 가 선언된 경우
if (to.matched.some((route) => route.meta.requiredAuth)) { if (to.matched.some((route) => route.meta.requiredAuth)) {
//2. 로그인 체크용 //2. 로그인 체크용
const access_token = jwt.getAccessToken() const access_token = tokenService.getLocalAccessToken()
if (access_token === null && jwt.getRefreshToken() !== null) { if (access_token === null && tokenService.getLocalRefreshToken() !== null) {
//refreshToken은 있고 accessToken이 없을 경우 토큰 재발급 요청 //refreshToken은 있고 accessToken이 없을 경우 토큰 재발급 요청
this.$store.dispatch('refreshToken') this.$store.dispatch('refreshToken')
} else if (access_token) { } else if (access_token) {

View File

@ -1,7 +1,36 @@
import api from '../interceptors/auth.api' import api from '../interceptors/auth.api'
import tokenService from './token.service'
class AuthService { class AuthService {
async login(params) { async login(email, password) {
return await api.get('/auth/login', params) return await api
.post('/auth/login', { email: email, password: password })
.then((response) => {
const { data } = response
if (data.access_token) {
tokenService.setUser(data)
}
return data
})
}
logout() {
tokenService.removeUser()
}
async register({ email, password, name }) {
return api
.post('/auth/register', {
email,
password,
name
})
.then((response) => {
const { data } = response
if (data.access_token) {
tokenService.setUser(data)
}
return data
})
} }
} }
export default new AuthService() export default new AuthService()

View File

@ -1,43 +0,0 @@
import VueCookies from 'vue-cookies'
const getAccessToken = () => {
return VueCookies.get('access_token')
//return window.localStorage.getItem(key)
}
const getRefreshToken = () => {
return VueCookies.get('refresh_token')
//return window.localStorage.getItem(key)
}
const saveAccessToken = (access_token) => {
VueCookies.set('access_token', access_token.token, access_token.expireIn)
//window.localStorage.setItem(key, token)
}
const saveRefreshToken = (refresh_token) => {
VueCookies.set('refresh_token', refresh_token.token, refresh_token.expireIn)
//window.localStorage.setItem(key, token)
}
const destroyToken = () => {
VueCookies.remove('access_token')
VueCookies.remove('refresh_token')
//window.localStorage.removeItem(key)
}
const getAuthorizationHeader = (config) => {
//Request시 헤더에 로그인정보 추가하기 위함
if (getAccessToken()) {
//For Backend
config.headers.common['Authorization'] = 'Bearer ' + getAccessToken()
// for Node.js Express back-end
// config.headers["x-access-token"] = getAccessToken();
}
return config
}
export default {
getAccessToken,
getRefreshToken,
saveAccessToken,
saveRefreshToken,
destroyToken,
getAuthorizationHeader
}

View File

@ -1,7 +1,15 @@
import api from '../interceptors/todo.api' import api from '../interceptors/todo.api'
import tokenService from './token.service'
class TodoService { class TodoService {
async setDatas(params) { async login(params) {
return await api.get('/todo', { params: params }) console.log(params)
return await api.get('/todo', { params: params }).then((response) => {
const { data } = response
if (data.access_token) {
tokenService.setUser(data)
}
return data
})
} }
} }
export default new TodoService() export default new TodoService()

View File

@ -1,8 +1,4 @@
class TokenService { class TokenService {
getLocalRefreshToken() {
const user = JSON.parse(localStorage.getItem('user'))
return user?.refreshToken
}
getLocalAccessToken() { getLocalAccessToken() {
const user = JSON.parse(localStorage.getItem('user')) const user = JSON.parse(localStorage.getItem('user'))
return user?.accessToken return user?.accessToken
@ -12,6 +8,10 @@ class TokenService {
user.accessToken = token user.accessToken = token
localStorage.setItem('user', JSON.stringify(user)) localStorage.setItem('user', JSON.stringify(user))
} }
getLocalRefreshToken() {
const user = JSON.parse(localStorage.getItem('user'))
return user?.refreshToken
}
getUser() { getUser() {
return JSON.parse(localStorage.getItem('user')) return JSON.parse(localStorage.getItem('user'))
} }

View File

@ -1,4 +1,4 @@
import jwt from '../service/jwt' import tokenService from '../service/token.service'
//참조: https://yamoo9.github.io/axios/guide/interceptors.html //참조: https://yamoo9.github.io/axios/guide/interceptors.html
// count state 속성 추가 // count state 속성 추가
@ -10,28 +10,24 @@ const getters = {
return state.takens return state.takens
}, },
isAuthenticated: function () { isAuthenticated: function () {
console.log('isAuthenticated:' + jwt.getAccessToken()) const isAuthenticated =
return !!jwt.getAccessToken() !!tokenService.getLocalAccessToken() ||
!!tokenService.getLocalRefreshToken()
return isAuthenticated
} }
} }
const mutations = { const mutations = {
register: function (state, tokens) { register: function (state, tokens) {
state.takens = tokens state.takens = tokens
jwt.saveAccessToken(tokens.access_token)
jwt.saveRefreshToken(tokens.refresh_token)
}, },
login: function (state, tokens) { login: function (state, tokens) {
state.takens = tokens state.takens = tokens
jwt.saveAccessToken(tokens.access_token)
jwt.saveRefreshToken(tokens.refresh_token)
}, },
logout: function (state) { logout: function (state) {
state.takens = {} state.takens = {}
jwt.destroyToken()
}, },
reload: function (state, access_token) { reload: function (state, access_token) {
state.takens.access_token = access_token state.takens.access_token = access_token
jwt.saveAccessToken(access_token)
} }
} }
const actions = { const actions = {

View File

@ -5,12 +5,6 @@ import TodoStore from './todo.store'
Vue.use(Vuex) Vue.use(Vuex)
//인터셉터 정의 및 Storage 연동
import setupAuthInterceptor from '../interceptors/auth.Iterceptor'
setupAuthInterceptor(AuthStore)
import setupTodoInterceptor from '../interceptors/todo.Iterceptor'
setupTodoInterceptor(TodoStore)
export default new Vuex.Store({ export default new Vuex.Store({
modules: { modules: {
AuthStore, AuthStore,