강디너의 개발 일지

Vue - Login 세션 유지하기 본문

Javascript/Vue.js

Vue - Login 세션 유지하기

강디너 2019. 11. 24. 15:54
728x90

사용할 기능들

1. navigation guard - 뷰 라우터 내비게이션 가드

2. axios Intercept - axios 설정

3. Vue cookies

4. vuex

 

 

토큰을 이용한 로그인 유지 flow는 아래 사진과 같습니다.

 

참고 - 쉽게 설명해주셨습니다.

https://tansfil.tistory.com/59

 

쉽게 알아보는 서버 인증 2편(Access Token + Refresh Token)

<26$ JWT 티셔츠 > 안녕하세요! 이전 포스팅에는 크게 세션/쿠키 인증, 토큰 기반 인증(대표적으로 JWT)에 대하여 알아보았습니다. 저희가 앱, 웹 혹은 서버 개발을 하면서 꼭 사용하게 되는 인증(Authorization)..

tansfil.tistory.com

 

더 더 검색해보니

 

사용자가 로그인하면 -> 서버측에서 Access Token과 Refresh Token을 발급해주고 -> 프론트로 보냅니다.

Access Token 의 만료기간은 보통 30분, Refresh Token 의 발급 기간은 보통 2주~한 달이라고 합니다.

그 이유는 Access Token이 만약 탈취되더라도 피해를 최소화하기 위해서라고 합니다.

 

그 후 Refresh Token은 DB에 저장하고 -> Refresh Token을 들고 오면 -> Access Token을 발급해주는 방식입니다.

 

저는 JWT을 만드는건 저번에 했고... Refresh Token을 만들어서 DB에 저장하기... 귀차니즘 발동 !

 

아무튼 ! 저는 token과 refresh_token을 대애충 만들었습니다.

app.post('/testLogin', (req, res)=>{
    return res.status(200).json({
      'data': {
        'token': 'this_is_token',
        'refresh_token': 'this_is_refresh_token'
      },
      'status': 200,
      'msg': 'success'
  });
});

app.post('/testRefreshToken', (req, res)=>{
  return res.status(200).json({
    'data':{
      'token' : 'new_token',
    },
    'status': 200,
  });
});

 

이쁘게 만들어진 토큰을 보고 싶으시다면!

아래 글 보시고 토큰을 만들어보시기 바랍니다.

https://kdinner.tistory.com/30

 

프로젝트_v7 Express JWT으로 로그인 인증 + 모듈화

JWT 으로 로그인 인증을 하겠습니다. 우선 jsonwebtoekn 모듈을 다운받겠습니다. 이 모듈은 jwt를 편하게 만들수 있도록 도와줍니다. JWT 로그인 1. user 테이블에서 user_pwd를 가져온 뒤 2. hash 해서 hash 한..

kdinner.tistory.com

 

 

구성한 페이지는 간단합니다.

 

: 권한이 있어야 들어가는 페이지 + 통신 테스트를 위한 페이지

어바웃 : 권한이 있어야 들어가는 페이지

로그인 : 권한이 없어도 들어가지는 페이지

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home,
  },
  {
    path: '/login',
    name: 'login',
    component: Login,
    meta: { unauthorized : true }
  },
  {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

 

라우터 권한 설정을 위해서 뷰 라우터 내비게이션 가드를 사용합니다.

https://router.vuejs.org/kr/guide/advanced/navigation-guards.html

router.beforeEach( async(to, from, next) => {

  if(VueCookies.get('token')===null && VueCookies.get('refresh_token') !== null){
    await refreshToken();
  }

  if (to.matched.some(record => record.meta.unauthorized) || VueCookies.get('token')){
    return next();
  }

    alert('로그인 해주세요');
    return next('/login');
})

 

1. 쿠키에 token값이 없을 경우 + Refresh Token 값이 있을 경우 -> 토큰 재발급 함수 실행

2. 쿠키에 token값이 있거나 ~ unauthorized 가 참일 경우 -> 다음 페이지로 이동

3. 아무것도 아닐 경우 (token값도 없고 Refresh Token도 없고) -> 로그인 페이지로 이동

 

라우터뿐만 아니라!

사용자가 그 페이지 그대로 가만히 있다가 토큰이 만료되었는데, 이 상태에서 통신 요청을 할 경우 !

Refresh Token을 이용해서 Access Token을 갱신 + 통신 요청 그대로 넣어줘야 합니다. 그렇게 해야지 사용자는 끊김 없이 바로 원하는 결과를 볼 수 있을 것이고 불편함이 없을 것입니다.

그렇게 하기 위해서는 axios Intercept를 설정해야 합니다.

import axios from 'axios';
import VueCookies from 'vue-cookies';
import { refreshToken } from '../service/login'

axios.defaults.baseURL = 'http://localhost:3000';

// Add a request interceptor
axios.interceptors.request.use(async function (config) {
    // Do something before request is sent
    config.headers.token = VueCookies.get('token');
    config.headers.refresh_token = VueCookies.get('refresh_token');
  
    console.log(config);
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
  }, async function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    console.log('에러일 경우', error.config);
    const errorAPI = error.config;
    if(error.response.data.status===401 && errorAPI.retry===undefined){
      errorAPI.retry = true;
      console.log('토큰이 이상한 오류일 경우');
      await refreshToken();
      return await axios(errorAPI);
    }
    return Promise.reject(error);
  });


export default axios;

위의 코드를 보시면

요청 전, 후로 나누어지며 https://github.com/axios/axios여기에서 자세히 설정을 볼 수 있습니다.

 

더 자세히 보시면(수정)

요청 후 에러가 401 에러고 retry 하지 않았다면 refresh 토큰을 얻습니다. 그런 다음 다시 axios를 태워서 새로운 토큰을 가지고 요청을 합니다. 만약 그것도 에러일 경우 return Promise.reject 되며, 정상일 경우 성공 데이터를 반환합니다.

 

실행 화면

1.  로그인 안 하고(쿠키에 정상적인 토큰 없이) / 로 가면 로그인하라고 보냅니다.

 

2. 로그인 버튼 클릭 시 토큰 값을 받아와서 쿠키에 저장

3. 토큰 잘 들어가 있는지 확인

 

테스트를 위해 token만 지운 후 통신 테스트

 

4. Access Token 값이 없을 때 에러를 받아서 바로 연결시켜줌으로써 사용자는 통신이 끊기는지 모름

 

백엔드쪽 코드입니다.

간단하게 토큰 값이 new_token이 아닐 경우 인증이 안된다고 오류 처리해버렸습니다.

리프레시로 받은 코드 값 아니면 결과로 받을 수 없습니다.(서비스에서는 이렇게 관리하시면 안 됩니다)

app.post('/testCall', (req, res)=>{
  console.log(req.headers.token);
  if(req.headers.token!=='new_token'){
    return res.status(401).json({
      'data':{
        'data' : '인증 안됨',
      },
      'status': 401,
    });
  }
  return res.status(200).json({
    'data':{
      'data' : 'test_call_success',
    },
    'status': 200,
  });
});

 

프론트 소스

https://github.com/DinnerKang/study_vue/tree/master/auth

 

백엔드 소스

https://github.com/DinnerKang/express-project

 

 

후기.

신규 프로젝트에서 인증 설정을 다루느라 지지고 볶고 했었는데, 구현했던 것 중 잘 안 풀렸던 것을 위주로 간단하게 다시 구현해봤습니다. 사용자가 로그인 세션이 끊겼다는 것을 느끼지 못하게 하는 것이 포인트인 것 같습니다. 이런 중요한 포인트를 알려주신 제 사수님께 감사의 말씀 올리며... 포스팅 거리를 주셔서 감사하옵니다.

아 ! 그리고 실제 서비스에서는 저렇게 막 노출시켜서는 안 됩니다!

 

감사합니다.

 

커피한잔...
Comments