ㅇㅇㅈ Blog

프론트엔드 수행중

0%

JWT 로그인

JWT 로그인 방식

서버에 로그인을 하면 토큰을 발급해주어
사용자가 서비스를 이용할때 이 토큰을 요청에 포함시켜 사용자를 인증한다.
근데 이 토큰엔 만료시간이 있다
이 프로젝트에서는 이 토큰의 만료시간이 15분이다
그래서 토큰이 만료되면 리프레시 토큰으로 다시 새로운 access_token을 발급 받아야 한다

오잉…..

access_token이 만료되면.. 리프레시 토큰으로 새롭게 발급 받으라고!?

일단 로그인할때 response 받은 access_tokenrefresh_token을 저장하자

시나리오 1.

  1. 로그인 할 때 발급 받은 access_token은 리덕스에, refresh_token은 쿠키에 저장한다
  2. hoc를 이용해 access_token이 만료되면 새롭게 발급받고 토큰이 필요한 컴포넌트에 props로 넘겨준다

react-cookie라는 라이브러리를 통해 쿠키에 쉽게 저장 할 수 있었다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// settingSessions.js
import { Cookies } from "react-cookie";
const cookies = new Cookies();

// 로그인 할때 받은 리프레시토큰을 인자로 받아 쿠키에 저장한다
export const setRefreshToken = (refreshToken) => {
const today = new Date();
const expireDate = today.setDate(today.getDate() + 7);

return cookies.set("refresh_token", refreshToken, {
sameSite: "strict",
path: "/",
expires: new Date(expireDate),
});
};

// 쿠키에 저장된 토큰을 가져온다
export const getCookieToken = () => {
return cookies.get("refresh_token");
};

// 쿠키에 저장된 토큰을 삭제한다
export const removeCookieToken = () => {
return cookies.remove("refresh_token", { sameSite: "strict", path: "/" });
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// AccessToken.jsx

import React, { useEffect, useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getCookieToken } from "@util/settingSessions";
import { refreshTokenFunc } from "@api/loginApi";
import { setAccessTokenAction } from "@redux/user/userSlice";

const AccessToken = (SpecificComponent) => {
const AccessToken = (props) => {
const accessToken = useSelector((state) => state.user.accessToken);
const userLogin = useSelector((state) => state.user.userLogin);

const dispatch = useDispatch();

const getCookie = useCallback(async () => {
const refresh = await getCookieToken();
const access_token = await refreshTokenFunc(refresh);
await dispatch(setAccessTokenAction(access_token));
}, [userLogin]);

useEffect(() => {
if (userLogin) {
getCookie();
}
}, []);
return (
<SpecificComponent
{...props}
accessToken={accessToken}
userLogin={userLogin}
/>
);
};
return AccessToken;
};

export default AccessToken;

조건문 작성이 좀 이상하긴 했다.. 유저가 로그인 되어있으면 리프레시 토큰을 호출했으니
그리고 또 다른 문제가 호출이 너무 많이 된다는 거다..
중간에 조건문 작성을 좀 바꿨었는데 안되서 다른 방식을 택했다…

시나리오 2.

  1. axios interceptor를 이용해서 토큰이 만료되면 리프레시토큰으로 다시 호출 해온다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

clientServer.interceptors.response.use(
async (response) => {
return response;
},
async (error) => {
const {
config,
response: { status },
} = error;
if (status === 401) {
if (
error.response.data.msg === "Token has expired" ||
error.response.data.msg === "Missing Authorization Header"
) {
const originalRequest = config;
const refreshToken = await getCookieToken();
const access_token = await refreshTokenFunc(refreshToken);

clientServer.defaults.headers.common.Authorization = `Bearer ${access_token}`;
originalRequest.headers.Authorization = `Bearer ${access_token}`;
return axios(originalRequest);
}
}

return Promise.reject(error);
}
);

interceptor를 이용한 refresh 토큰을 검색해보니 여러 정보가 떴다
보면서 작성하긴 했는데 이게 제대로 되는지 사실 애매했다.
어떨때는 잘 불러오는데 어떨때는 …. 새로고침을 해줘야 토큰을 다시 불러 올 때도 있었다.
axios interceptor에 대해서 좀 더 공부해 볼 필요가 있다