2021. 12. 11. 00:22ㆍSpring Security/Vue 3 Authentication with JWT
로그인 처리는 사용자가 로그인 시 JWT를 이용하여서 토큰을 발급받은 후 Local Storage에 서버로부터 발급받은 AccessToken과 사용자 정보를 저장하게 된다.
저장된 정보는 로그인 후 페이지 접근 시 Request Header에 AccessToken을 전송하여서 사용자 인증을 처리하게 된다.
서버 쪽 프로세스는 아래 글을 참고하여주시기 바랍니다.
https://jydlove.tistory.com/63?category=1031676
변경된 서버 프로세스는 쿠키를 사용하지 않는다. 이유는 클라이언트의 Local Storage를 사용하기 때문이다.
로그인 화면
로그인 후 화면 아래 보면 user 라는 정보에 로그인 후 전달받은 토큰 정보, 사용자 권한, 사용자 정보가 저장된 것을 확인할 수 있습니다.
소스
앞에 프로젝트 설정 시 composables 폴더는 생성이 되지 않습니다.
composables 폴더는 컴포넌트의 재사용성을 위해 별도로 만든 폴더입니다.
views/auth/Login.vue
로그인 화면에 해당하는 화면 소스
<template>
<form @submit.prevent="handleSubmit">
<h3>로그인</h3>
<input type="email" placeholder="Email" v-model="username">
<input type="password" placeholder="Password" v-model="password">
<div v-if="error" class="error">{{ error }}</div>
<button v-if="!isPending">로그인</button>
<button v-if="isPending" disable>Loading</button>
</form>
</template>
<script>
import useLogin from '@/composables/useLogin'
import { useStore } from 'vuex'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
export default {
setup() {
const{ error, login, isPending } = useLogin()
const store = new useStore()
const router = useRouter()
const username = ref('')
const password = ref('')
const handleSubmit = async () => {
await login(username.value, password.value)
if (store.state.auth.status.loggedIn) {
router.push({ name: 'Profile' })
}
}
return { username, password, handleSubmit, error, isPending }
}
}
</script>
<style>
</style>
해당 소스 부분이 실행이 되면 아래의 순서로 로그인 처리가 진행됩니다.
const handleSubmit = async () => {
// 사용자 로그인 처리
await login(username.value, password.value)
}
composables/useLogin.js
실제 서버와의 통신을 통하여서 로그인 성공 시 응답 값을 받아서 Local Storage에 저장하는 역할과 vuex를 이용하여서 사용자의 로그인 여부를 state에 저장 함으로써 다른 컴포넌트에서도 사용자의 로그인 여부를 판단할 수 있다.
import { ref } from 'vue'
import store from "../store"
const error = ref(null)
const isPending = ref(false)
const login = async (username, password) => {
error.value = null
isPending.value = true
try {
await store.dispatch('auth/login', {username, password})
error.value = null
isPending.value = false
} catch(err) {
error.value = '로그인 정보가 올바르지 않습니다.'
isPending.value = false
}
}
const useLogin = () => {
return { error, login, isPending }
}
export default useLogin
store/index.js
store 객체를 생성하고 모듈을 관리한다.
프로젝트에서는 로그인과 로그아웃을 관리하는 auth 모듈을 만들어서 별도로 관리한다.
import { createStore } from "vuex";
import { auth } from "./auth.module";
const store = createStore({
modules: {
auth,
},
});
export default store;
store/auth.module.js
소스를 보기 전에 vuex의 store에 대한 개념은 아래의 그림을 참고하자
Actions: Backend API 호출
Mutatios: 뜻은 변이라는 뜻이지만 쉽게 말하자면 Backend API 호출 결괏값을 파라미터로 전달받은 후 state에 값을 저장하게 된다.
위의 개념을 보고 아래 소스를 보면 이해하기 쉬울 것입니다.
아래 소스는 Backend Login API 호출 후 결과로 사용자 정보를 담고 있는 user 객체를 받은 후 mutations에서 user 객체와 로그인 상태를 저장하게 됩니다.
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
const initialState = user
? { status: { loggedIn: true }, user }
: { status: { loggedIn: false }, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, {username, password}) {
return AuthService.login(username, password).then(
user => {
commit('loginSuccess', user);
return Promise.resolve(user);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
logout({ commit }) {
AuthService.logout();
commit('logout');
}
},
mutations: {
loginSuccess(state, user) {
state.status.loggedIn = true;
state.user = user;
},
loginFailure(state) {
state.status.loggedIn = false;
state.user = null;
},
logout(state) {
state.status.loggedIn = false;
state.user = null;
}
},
getters: {
isLoggedIn: state => state.loginSuccess
}
};
services/user.service.js
Backend API 인 로그인 API를 호출한다.
로그인 API는 아래 그림과 같다.
import axios from 'axios';
const API_URL = 'http://localhost:7070/';
class AuthService {
/**
* 로그인
*/
login(username, password) {
return axios.post(API_URL + 'signin', {
username: username,
password: password
})
.then(response => {
if (response.data.accessToken) {
localStorage.setItem('user', JSON.stringify(response.data));
}
return response.data;
});
}
/**
* 로그아웃
*/
logout() {
// LocalStorage 사용자 정보
let user = JSON.parse(localStorage.getItem('user'))
let data = {
username: user.username
}
return axios.post(API_URL + 'signout', JSON.stringify(data), {
headers: {
"Content-Type": 'application/json',
},
})
.then(response => {
console.log(response)
localStorage.removeItem('user');
});
}
}
export default new AuthService();
여기까지 실행이 되면 로그인 API 호출 후 처리결과를 vuex store에 저장하게 됩니다.
마지막으로 아래 소스와 같이 store의 로그인 상태가 ture인 경우 vue-router를 이용하여서 Profile 페이지로 이동 처리합니다.
const handleSubmit = async () => {
// 로그인 처리
await login(username.value, password.value)
// 로그인 상태 이면 Profile 화면으로 이동
if (store.state.auth.status.loggedIn) {
router.push({ name: 'Profile' })
}
}
'Spring Security > Vue 3 Authentication with JWT' 카테고리의 다른 글
Axios Interceptor 를 이용한 토큰 갱신 및 권한 처리 (0) | 2021.12.12 |
---|---|
Vue3 프로젝트 설정 (0) | 2021.12.07 |
Vue3 프로젝트 개요 (0) | 2021.12.06 |