새소식

개발/Frontend

axios.interceptors를 사용해 JWT유효성 관리

  • -

Axios.interceptors

별도의 Library가 아닌 axios에 포함된 기능이다. 요청이나 응답 전에 무엇인가를 수행해주거나, 오류 발생시에 수행할 것들을 미리 정의해둘 수 있다.

  • axios.interceptors.request.use()
    axios.interceptors.request.use(function (config){
    	// Do something before request is sent
    	return config;
    }, function (error){
    	// Do something with Request error
    	return Promise.reject(error);
    });​
  • axios.interceptors.response.use()
    axios.interceptors.response.use(function (response){
    	// Do something with response data
    	return response;
    }, function (error){
    	// Do something with Response error
    	return Promise.reject(error);
    });​
  • Devtool로 본 나만의 axios.interceptors 처리 과정

 

  • 사용
    axios.interceptors.response.use(
    	// 정상 응답처리
    	(config) => {
    		return config
    	},
    	// 오류 발생시
    	function (error) {
    		const originalRequest = error.config;
    		//AccessToken이 만료됬고, RefreshToken이 유효하다면
    		if(validateTimeRefreshtoken() &&!validateTimeAccesstoken()){
    			// Refreshtoken을 통해 Accesstoken을 재발급한다.
    			return getAccesstokenWithRefreshtoken()
    				.then(res => {
    					// Response가 정상이라면
    					if(res.status === 200){
    						console.log(['Inteceptors.Response] got succeed');
    						sessionStorage.setItem('accessToken', res.data.accessToken);
    						sessionStorage.setItem('memberId', res.data.memberId);
    						sessionStorage.setItem('role', res.data.role);
    						axios.defaults.header.common['accessToken'] =
    						  sessionStorage.getItem('accessToken');
    						// 실패했던 요청을 다시 수행한다.
    						return axios(originalRequest);
    					}
    				})
    		} // Refreshtoken이 만료됬을때
    		else if (!validateTimeRefreshtoken()){
    			sessionStorage.clear();
    			localStorage.clear();
    			window.location.href = "http://localhost:3000";
    			alert("세션 만료 다시 로그인 해주세요.");
    		}
    		return Promise.reject(error);
    	}
    
    	);​

axios.create()

  • 사용자 정의 axios 객체를 만든다.
    const loginInstance = axios.create(config => {
    	config.headers['Content-Type'] = 'application/json';
    })​

jwt-decode

  • JWT decoding을 도와주는 Library이다.
  • Terminal에서 아래 명령어로 설치한다.
npm i jwt-decode
import * as jwt_decode from 'jwt-decode';

var decodePayload = jwt_decode(accessToken, {payload: true});

 

validateTimeAccessToken(), validateTimeRefreshToken()

const validateTimeAccesstoken = () => {
	// sessionStorage에서 accessToken을 가져온다.
	const accessToken = sessionStorage.getItem('accessToken');
	// jwt를 decode 하여 payload를 추출한다.
	const decodePayload = jwt_decode(accessToken, {payload: true});

	// exp가 UNIX Time으로 나오기 때문에 변환을 해준다.
	var exp = (new Date(decodePayload.exp * 1000).getTime());
	var now = new Date().getTime();

	// 변환된 값을 비교하고 Boolean type을 반환한다.
	if(now < exp){
		console.log("AccessToken is valid");
		return true;
	} else {
		consoel.log("AccessToken is invalid");
		return false;
	}
}

 

getAtwithRefreshToken()

export const getAccesstokenWithRefreshtoken = () => {
	getAtInstance.post(baseURL + "/getAt", "", 
	{
		headers: { 'refreshToken' : localStorage.getItem('refreshToken')}
	})
}

 

3. 상세 흐름

A. (React) Browser에서 Login을 한다. (Id, Password) 

B. (React) axios에서 Login api를 호출한다.(axios.interceptors.request로 전역 설정된 axios를 호출하지 않기 위해 새로운 axios 인스턴스(loginInstance)를 생성하고 해당 인스턴스로 호출한다.)

C. (Spring) Controller에서 받고, Params의 (Id + Pw) = DB의(Id + Pw) 비교한다.
	C_a : 일치시 Id와 Roles를 Claims에 넣은뒤, JWT(AccessToken(At),RefreshToken(Rt))를 생성한다.
	C_b : 불일치시 Exception을 던지고, Failcode 반환한다.
	C_b2 : Browser에서 실패 alert 표시한다.

D. (Spring) Map<String,String>구조의 ‘data’에 At, memberId, Role, Rt을 넣고 반환한다.

E. (React) Payload.data로 넘어온 값 저장한다.
- sessionStorage(At, memberId, Role) 
- localStorage(Rt)

F. (React) 정보요청시 Request Header에 AccessToken을 키로 담아서 요청한다.
	F_a : 요청 이후 error 발생시, axios.interceptors.response에서 오류 발생을 감지한다.
	F_a_2 : (axios.interceptors.response) At와 Rt가 유효한지 검증한다.

	F_a_2-a1 : AccessToken만 만료됬고 RefreshToken은 유효하다면, Rt를 request header에 넣어서 Back-End로 보낸다.
	F_a_2-a2 : Back-End에서 RefreshToken을 Parsing한 뒤, Id 값을 추출한다. 추출한 Id로 해당 Column을 조회한뒤, RefreshToken값을 비교한다.
	F_a_2-a3 : 일치하다면 AccessToken을 재발급해서 전송하고 ‘E’에서의 행동과 동일하게 처리한다.
	
	F_a_2-b1 : AccessTokne과 RefreshToken이 전부 만료됬다면, sessionStorage와 localStorage의 내용을 지우고 초기화면(로그인)으로 이동시킨 후 세션 만료에 대한 alert를 표시한다.

G. (Spring) 요청을 받은 Back-End는 AccessToken을 검증하고 유효 하다면 요청 정보를 제공한다.


F ~ G의 과정을 계속해서 반복한다.

'개발 > Frontend' 카테고리의 다른 글

Vue 따라해보기  (0) 2020.12.01
React js VS Vue js  (0) 2020.03.31
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.