refresh token axios interceptorFrontend/리액트 개발 꿀팁2022. 7. 20. 06:27
Table of Contents
Why?
- refresh token과 access token을 사용할 때, 인증이 필요한 api 요청 시 access token이 만료되면 refresh token을 이용해서 access token을 받아온 후 요청하려고 했던 api를 다시 요청하는 axios interceptor를 구현해보자.
- 만약, refresh token이 만료되면 로그인 페이지로 이동시키자!
intercetor 구현
- 서버에서는 refresh token과 access token을 header로 각각 X-REFRESH-TOKEN과 X-ACCESS-TOKEN으로 준다고 가정.
- refresh token과 access token 둘 다 백엔드의 보안 미들웨어를 통과하는데 필요하다 가정
- 클라이언트는 refresh token을 local storage에 access token을 cookie에 저장한다고 가정
- refresh token으로 access token을 재발급 받는 api는 header로 refresh token을 넘겨준다고 가정
- refresh token으로 access token 재발급에 성공하면 원래 api의 header에 새로 발급받은 refresh token과 access token을 넘겨준다.
- 서버에서 refresh token과 access token을 둘 다 받아야 spring security를 뚫을 수 있다고 구현했기 때문!
- 각 백엔드 프로젝트마다 인증에 필요한 token과 token을 받는 위치와 방식이 다르므로 access token이 만료된 후 재발급에 성공하면 refresh token과 access token을 재발급 받은 후 원래 api로 돌아가는 방식만 기억하자!
import axios from "axios";
import { Cookies } from "react-cookie";
export const instanceWithToken = axios.create({ baseURL: "https://tryaz.shop" });
instanceWithToken.interceptors.request.use(
(config) => {
const refreshTokenState = JSON.parse(localStorage.getItem("X-REFRESH-TOKEN"))
? JSON.parse(localStorage.getItem("X-REFRESH-TOKEN"))
: "";
const refreshToken = refreshTokenState["X-REFRESH-TOKEN"];
const cookies = new Cookies();
const accessToken = cookies.get("X-ACCESS-TOKEN");
config.headers["X-REFRESH-TOKEN"] = `BEARER ${refreshToken}`;
config.headers["X-ACCESS-TOKEN"] = `BEARER ${accessToken}`;
return config;
},
(error) => {
return Promise.reject(error);
},
);
instanceWithToken.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
console.log(error);
const refreshTokenState = JSON.parse(localStorage.getItem("X-REFRESH-TOKEN"))
? JSON.parse(localStorage.getItem("X-REFRESH-TOKEN"))
: "";
const refreshToken = refreshTokenState["X-REFRESH-TOKEN"];
const cookies = new Cookies();
// 원래 api
const originalRequest = error.config;
/*
API 요청 도중 access token이 만료되면 refresh token을 가지고 재발급 받고
원래 호출하려던 api에 새로운 access/refresh token을 넣어서 보냄
*/
if (error.response.data === "JwtAuthFilter-Access-expired") {
// referesh token을 가지고 access token을 발급받는 api 호출
await axios
.get("https://tryaz.shop/api/user/refresh-re", {
headers: {
"X-REFRESH-TOKEN": "BEARER " + refreshToken,
},
})
// refresh token을 가지고 access token 발급에 성공
.then((result) => {
const newRefreshToken = result.headers["x-refresh-token"].split(" ")[1];
const newAccessToken = result.headers["x-access-token"].split(" ")[1];
localStorage.setItem("X-REFRESH-TOKEN", `{"X-REFRESH-TOKEN": "${newRefreshToken}"}`);
const daysToExpire = new Date(2147483647 * 1000);
cookies.set("X-ACCESS-TOKEN", newAccessToken, { expires: daysToExpire });
originalRequest.headers["X-REFRESH-TOKEN"] = `BEARER ${newRefreshToken}`;
originalRequest.headers["X-ACCESS-TOKEN"] = `BEARER ${newAccessToken}`;
})
// refresh token을 가지고 access token을 발급 받지 못하면 -> refresh token expired 되면 로그인 페이지로 이동
.catch((refresh_token_error) => {
// refresh와 access token삭제
localStorage.removeItem("X-REFRESH-TOKEN");
cookies.remove("X-ACCESS-TOKEN");
window.location.href = "/sign-in";
alert("로그인 시간이 만료되었습니다. 다시 로그인 해주세요.");
return false;
});
return axios(originalRequest);
// 다른곳에서 로그인해서 access token이 valid하지 않거나 refresh token이 DB와 다를 때
} else if (
error.response.data === "JwtAuthFilter-Access-Invalid" ||
error.response.data === "JwtAuthFilter-Refresh-Not_existDB" ||
error.response.data === "JwtAuthFilter-Refresh-Invalid"
) {
localStorage.removeItem("X-REFRESH-TOKEN");
cookies.remove("X-ACCESS-TOKEN");
window.location.href = "/sign-in";
alert("다른 곳에서 로그인 되었습니다. 다시 로그인 해주세요");
return false;
// refresh token이 만료되면 다시 로그인
} else if (error.response.data === "JwtAuthFilter-Refresh-expired") {
localStorage.removeItem("X-REFRESH-TOKEN");
cookies.remove("X-ACCESS-TOKEN");
window.location.href = "/sign-in";
alert("로그인 시간이 만료되었습니다. 다시 로그인 해주세요.");
return false;
// 다른곳에서 catch 할 수 있게 만들자!
} else {
return Promise.reject(error);
}
return Promise.reject(error);
},
);
'Frontend > 리액트 개발 꿀팁' 카테고리의 다른 글
PrivateRoute (0) | 2022.06.17 |
---|---|
axios interceptor (0) | 2022.06.17 |
form-data로 파일과 객체 보내기 (0) | 2022.06.17 |
ESLint/Prettier 설정 (0) | 2022.06.13 |
useState 주의사항 (0) | 2022.06.11 |
@덕구공 :: Duck9s'
주니어 개발자에욤
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!