Vue.js 3 & NodeJS/Vue 3

Vue CLI 레스트 API 인증 토큰 재발급 (액시오스 인터셉터) - Vue CLI REST API Authentication, Axios Interceptor Response

carrotweb 2022. 1. 23. 17:48
728x90
반응형

이번에는 axios(액시오스)의 인터셉터(interceptor) 중 응답(response)으로 처리해 보겠습니다.

Node.js 레스트 API 서버를 먼저 실행시킵니다.

콘솔을 실행하고 Node.js 레스트 API 서버 프로젝트가 있는 C:\workspaces\nodeserver\testrestapi 폴더로 이동합니다. 그리고 npm run 명령어를 실행합니다.

npm run start

 

 

axios(액시오스)의 인터셉터(interceptor) 응답(response) 추가하기

1. C:\workspaces\nodeserver\testvue\src\axios\index.js 파일에서 기존 axios.interceptors의 request로 처리한 부분을 주석 처리하거나 삭제합니다. 그리고 axios.interceptors의 response를 추가합니다. 테스트를 위해 콘솔 로그를 출력합니다.

import axios from 'axios';
import store from '@/store';

/*
axios.interceptors.request.use(async function (config) {
		// 요청이 전달되기 전에 처리
		// 로그인되었는지 확인한다.
		var isLogin = store.getters['loginStore/isLogin'];
		if (isLogin) {
			// accessToken이 만료되었는지 확인합니다.
			var isAccessTokenExpire = store.getters['loginStore/isAccessTokenExpire'];
			if (isAccessTokenExpire) {
				// accessToken이 만료되면 토큰 재발급을 진행합니다.
				try {
					// accessToken 재발급 요청
					await store.dispatch("loginStore/doRefreshToken");
					// 재발급된 accessToken으로 해더를 수정합니다.
					config.headers['Access-Token'] = store.state.loginStore.accessToken;
				} catch(err) {
					alert("다시 로그인을 해주시기 바랍니다.\n" + err.response.data.errormessage);
				}
			}
		}
		return config;
	}, function (error) {
		// 요청 오류에 대한 처리
		return Promise.reject(error);
	});
*/

axios.interceptors.response.use(function (response) {
		// 응답 데이터로 처리
		console.log("axios.interceptors.response");
		return response;
	}, function (error) {
		// 응답 오류에 대한 처리
		console.log("axios.interceptors.response");
		// accessToken이 만료되었는지 확인합니다.
		var isAccessTokenExpire = store.getters['loginStore/isAccessTokenExpire'];
		console.log("AccessTokenExpire = " + isAccessTokenExpire);
		return Promise.reject(error);
	});

export default axios;

 

2. 콘솔을 실행하고 Vue 프로젝트가 있는 C:\workspaces\nodeserver\testvue 폴더로 이동합니다. 그리고 콘솔에서 npm run 명령어를 실행합니다.

npm run serve

 

3. 웹 브라우저에서 "http://localhost:8080/board"를 입력합니다. 그러면 응답할 때마다 콘솔에 "axios.interceptors.response"가 출력되는 것을 확인할 수 있습니다.

 

4. 10분 후 게시판 쓰기 페이지로 이동한 후 제목과 내용을 입력하고 저장 버튼을 클릭합니다. 그러면 accessToken이 만료되어 콘솔 로그에 출력됩니다. 그리고 레스트 API 서버로 403 응답 코드로 응답이 들어오는 것을 확인할 수 있습니다.

 

Node.js 레스트 API 서버의 C:\workspaces\nodeserver\testrestapi\authmiddleware.js 파일을 보면 accessToken이 요청 헤더에 없거나 accessToken이 유효하지 않으면 응답 코드로 403과 "Authentication fail" 메시지를 리턴합니다.

const jwt = require('jsonwebtoken');
const config = require('./config.js');

const authMiddleware = async (req, res, next) => {
	const accessToken = req.header('Access-Token');
	if (accessToken == null) {
		res.status(403).json({success:false, errormessage:'Authentication fail'});
	} else {
		try {
			const tokenInfo = await new Promise((resolve, reject) => {
				jwt.verify(accessToken, config.secret, 
					(err, decoded) => {
						if (err) {
							reject(err);
						} else {
							resolve(decoded);
						}
					});
			});
			req.tokenInfo = tokenInfo;
			next();
		} catch(err) {
			console.log(err);
			res.status(403).json({success:false, errormessage:'Authentication fail'});
		}
	}
}

module.exports = authMiddleware;

 

 

error 객체의 response를 콘솔 로그로 출력해 보면 응답 코드(status)와 응답 메시지(data.errormessage)를 확인할 수 있습니다.

console.log(error.response);

 

5. 응답 코드가 403이고 응답 메시지가 "Authentication fail"때만 토큰 재발급이 되도록 처리합니다. 그리고 로그인되었는지 확인(accessToken이 요청 헤더에 없을 수 있습니다.) 한 후 에러가 발생한 요청 정보(config)를 가져옵니다.

accessToken이 재발급된 후 요청 정보의 headers['Access-Token']에 재발급된 accessToken으로 수정합니다.

그리고 다시 요청 정보로 axios를 통해 요청(request) 합니다.

동기화 처리를 위해 async와 await를 사용합니다.

import axios from 'axios';
import store from '@/store';

axios.interceptors.response.use(function (response) {
		// 응답 데이터로 처리
		return response;
	}, async function (error) {
		// 응답 오류에 대한 처리
		//console.log(error.response);
		if (error.response) {
			// 응답코드가 403이고 응답 메시지가 "Authentication fail"때만 처리합니다.
			if (error.response.status == 403 && error.response.data.errormessage == "Authentication fail") {
				// 로그인되었는지 확인한다.
				var isLogin = store.getters['loginStore/isLogin'];
				if (isLogin) {
					// 에러가 발생한 요청 정보를 가져옵니다.
					var axiosRequest = error.config;
					// accessToken이 만료되면 토큰 재발급을 진행합니다.
					try {
						// accessToken 재발급 요청
						await store.dispatch("loginStore/doRefreshToken");
						// 재발급된 accessToken으로 해더를 수정합니다.
						axiosRequest.headers['Access-Token'] = store.state.loginStore.accessToken;
						// 다시 요청 정보로 요청(request)합니다.
						return axios(axiosRequest);
					} catch(err) {
						alert("다시 로그인을 해주시기 바랍니다.\n" + err.response.data.errormessage);
					}
				}
			}
		}
		return Promise.reject(error);
	});

export default axios;

응답 코드가 403이고 응답 메시지가 "Authentication fail"때(accessToken이 유효하지 않을 때)만 처리함으로 토큰 만료 검사는 하지 않아도 됩니다.

6. 다시 웹 브라우저에서 "http://localhost:8080/board"를 입력합니다. 그리고 쓰기 버튼을 클릭합니다. Login 페이지에서 아이이와 패스워드를 입력하면 게시물 작성 페이지로 이동됩니다. 테스트를 위해 제목에 "테스트 제목4", 내용에 "테스트 내용4"를 입력하고 10분 후 등록 버튼을 누릅니다.

그러면 토큰이 재발급되면서 "Access-Token이 갱신되었습니다."라고 출력됩니다. 그리고 다시 호출되어 게시물이 정상적으로 등록됩니다.

 

반응형

 

axios(액시오스)의 인터셉터(interceptor) 무한 루프(Infinite Loop) 방지하기

다시 보낸 요청이 다시 실패하면 403 응답 코드를 계속 받게 되어 무한 루프 발생됩니다.

무한 루프를 방지하기 위해서 요청 정보(config)에 있는 _retry를 사용하여 한 번만 들어오게 하면 됩니다.

import axios from 'axios';
import store from '@/store';

axios.interceptors.response.use(function (response) {
		// 응답 데이터로 처리
		return response;
	}, async function (error) {
		// 응답 오류에 대한 처리
		//console.log(error.response);
		// 에러가 발생한 요청 정보를 가져옵니다.
		var axiosRequest = error.config;
		if (error.response && !axiosRequest._retry) {
			axiosRequest._retry = true;
			// 응답코드가 403이고 응답 메시지가 "Authentication fail"때만 처리합니다.
			if (error.response.status == 403 && error.response.data.errormessage == "Authentication fail") {
				// 로그인되었는지 확인한다.
				var isLogin = store.getters['loginStore/isLogin'];
				if (isLogin) {
					// accessToken이 만료되면 토큰 재발급을 진행합니다.
					try {
						// accessToken 재발급 요청
						await store.dispatch("loginStore/doRefreshToken");
						// 재발급된 accessToken으로 해더를 수정합니다.
						axiosRequest.headers['Access-Token'] = store.state.loginStore.accessToken;
						// 다시 요청 정보로 요청(request)합니다.
						return axios(axiosRequest);
					} catch(err) {
						alert("다시 로그인을 해주시기 바랍니다.\n" + err.response.data.errormessage);
					}
				}
			}
		}
		return Promise.reject(error);
	});

export default axios;

 

axios(액시오스)의 인터셉터(interceptor)를 이용하여 요청(request)이나 응답(response)에서 토큰 재발급을 처리하면 됩니다.

728x90
반응형