728x90
Token-Based Authentication
JSON Web Tokens(JWT)을 이용하여 front-end auth solution을 만드는 방법을 알아보자.
사용할 모듈은 Router, Vuex, Axios가 되겠다.
Authentication 이 작동하는 방식
- Client가 Server에 Login 요청
- UserId와 password가 일치한다면 Server는 JWT 토큰 생성하여 Client에 반환
- Client는 Server로 부터 받은 JWT 토큰은 local storage에 저장
- 추후 Client에서 API 호출 시 HTTP request header에 localstorage에 저장된 JWT 토큰 실어서 전송
- Client가 로그아웃한다면 로그아웃 API 호출 및 localstorage에서 JWT 토큰 제거.
JWT 토큰 구성
- 헤더 : Type(JWT), Hasing algorithm
- Payload : Info we're transmitting. ex.) iss(issuer: 'verlopert.com'), exp(expiresIn: '7d', admin(admin:'false')
- Signature : Hash of Header + Payload + Secret
VueJS Authentication 구현 과정
- Register users
- Login users
- /register와 /login api는 userData({token,email,name})을 반환한다.
- Vuex
- userData를 State에 저장한다.
- userData를 localStorage에 저장한다.
- token을 axios header default 설정으로 더해준다.
- Access protected data
- Logout
- Vuex State로부터 userData를 지운다
- localStorage로부터 userDAta를 지운다
- axios header default 설정에 token을 지워준다
User Registration
과정
- RegisterUser.vue를 생성한다.
- 유저 가입 정보(name,email,password)를 입력받는다.
-
//RegisterUser.vue ... methods:{ register(){ this.$store.dispatch('register',{ name:this.name, email:this.email, password:this.password }) .then(()=>{ this.$router.push({name:'dashboard'}); }) } }
- Vuex
- 유저 가입 정보를 서버로 전송한다. (/register api)
- 반환된 userData(token,email,name)을 State와 localStorage에 저장한다.
- Axios header에 토큰을 default 상태로 더해준다.
-
//store.js ... export default new Vuex.Store({ state: { user:null }, mutations:{ SET_USER_DATA(state,userData){ state.user = userData; localStorage.setItem('user',JSON.stringify(userData)); axios.defaults.headers.common['Authorization'] = `Bearer ${userData.token}` } }, actions:{ register({commit}, credentials){ return axios.post('//localhost:3000/register',credentials).then( ({data})=>{ commit('SET_USER_DATA',data); }) } } })
Login
과정
- RegisterUser.vue를 생성한다.
- 유저 email, password를 입력받는다.
-
//LoginUser.vue ... methods:{ login(){ this.$store.dispatch('login',{ email:this.email, password: this.password }) .then(()=>{ this.$router.push({name:'dashboard'}) }) } }
- Vuex
- 유저 가입 정보를 서버로 전송한다. (/login api)
- 반환된 userData(token,email,name)을 State와 localStorage에 저장한다.
- Axios header에 토큰을 default 상태로 더해준다.
-
//store.js ... export default new Vuex.Store({ state: { user:null }, getters:{ loggedIn(state){ return !!state.user; } } mutations:{ ... }, actions:{ ..., login({commit}, credentials){ return axios.post('//localhost:3000/login',credentaials) .then(({data})=>{ commit('SET_USER_DATA',data); }) } } })
Logout
- logout button과 logout method를 생성한다.
-
//AppNav.vue ... methods{ logout(){ this.$store.dispatch('logout') } }
-
- VueX
-
//store.js ... export default new Vuex.Store({ state: { user:null }, getters:{ ... } mutations:{ ..., CLEAR_USER_DATA(state){ state.user = null; localStorage.removeItem('user'); axios.default.headers.common['Authorization'] = null } }, actions:{ ..., logout({commit}){ commit('CLEAR_USER_DATA') } } })
-
- protected data(dashboard route) 접근 제한
- logout 되더라도 dashboard page에 여전히 남아있다.
- route에 Navigation Guard를 추가함으로써 해결할 수 있다.
-
//router.js const router = [ ... { path:'/dashboard', name:'dashboard', component: Dashboard, meta: { requiresAuth: true}, }, ... ]; router.beforeEach((to,from,next)=>{ const loggedIn = localStorage.getItem('user'); if(to.matched.some(record=>record.meta.requiresAuth) && !loggedIn){ // to.matched는 이동할 route와 match되는 route들 next('/') // redirect to homepage }else{ next() // direct to to } }) export default router;
Error Handling
어떤 에러가 발생할 수 있을까?
- User가 유효하지않은 id/password로 로그인 시도
- 회원가입 시 이미 가입된 Email일때
- 회원가입 시 password의 format이 유효하지않을때
Login Error Handling
//LoginUser.vue
...
methods:{
login(){
this.$store.dispatch('login',{
email:this.email,
password:this.password
})
.then(()=>{
this.$router.push('dashboard');
})
.catch(err=>{
this.error = err.response.data.error;
})
}
}
Register Error Handling
//RegiserUser.vue
...
methods:{
register(){
this.$store.dispatch('register',{
name:this.name,
email:this.email,
password:this.password
})
.then(()=>{
this.$router.push({name:'dashboard'});
})
.catch((err)=>{
this.error = err.response.data.error
})
}
}
- Client-side form validation 을 하고 싶다면 다음 Post에서 볼 수 있다.
- component내에서 error handling을 유지하도록 하기위해 VueX내에서 error handling을 하지 않는다
- 만약 여러 컴포넌트에서 사용하는 재사용 기능이라면 action내에서 state change를 위해 error handling을 해야할 것이다.
만약 너의 에러가 컴포넌트에서 UI상 보여지는게 목적이라면 컴포넌트내에서 error handling을 해야할 것이다.
- 만약 여러 컴포넌트에서 사용하는 재사용 기능이라면 action내에서 state change를 위해 error handling을 해야할 것이다.
Automatic Login
페이지가 refresh 되더라도 로그인 상태를 유지하게 만들어보자.
//main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import state from './vuex/store'
Vue.config.productionTip = false;
new Vue({
router,
store,
created(){ // created lifecycle hook 생성
const userString = localStorage.getItem('user');
if(userString){
const userData = JSON.parse(userString);
this.$store.commit('SET_USER_DATA',userData);
}
},
render:h => h(App)
}).$mount('#app');
web 이 생성되면 localStorage를 보고 vuex에 다시 저장한다.
만약 누군가가 localStorage에 임의의 token을 집어넣어서 권한이 없는데도 접근하면 어쩌지?
localStorage에 강제로 값을 집어넣었을때 이를 detect할수 있는 client-side 보안 기능을 만들어보자
//main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import state from './vuex/store'
import axios from 'axios'
Vue.config.productionTip = false;
new Vue({
router,
store,
created(){ // created lifecycle hook 생성
const userString = localStorage.getItem('user');
if(userString){
const userData = JSON.parse(userString);
this.$store.commit('SET_USER_DATA',userData);
}
axios.interceptors.respose.use(
response=> response,
error =>{
if (error.response.status == 401){
this.$store.dispatch('logout');
}
return Promise.reject(error)
}
},
render:h => h(App)
}).$mount('#app');
localStorage의 token값이 잘못된 상태로 서버에 protected api를 요청하면 401에러를 받는데
401에러를 받으면 강제로 로그아웃 시켜버린다.
'VueJS' 카테고리의 다른 글
Vue3 google SEO 등록하기 (0) | 2022.04.15 |
---|---|
VueJS 3.0 form validating (0) | 2022.01.08 |
AWS 에 Jenkins와 Nginx 이용하여 vue project 올리기 (0) | 2022.01.02 |
Vue 3 - 5. Props With Types (0) | 2021.12.28 |
Vue3 - 4.Custom type의 데이터 (0) | 2021.12.28 |