From 7ca217eafc4799764728834755b8d72e08656c94 Mon Sep 17 00:00:00 2001 From: novice1993 Date: Thu, 21 Sep 2023 14:45:26 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20Oauth=20=EA=B4=80=EB=A0=A8=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Oauth 로그인 액세스 토큰 만료시간 관려하여 자동 로그아웃 기능 구현 - 코드 가독성이 많이 떨어지는 상황으로 추후 리팩토링 예정 Issues #122 --- client/src/components/Logins/EmailLogin.tsx | 19 +-- client/src/page/MainPage.tsx | 172 ++++++++++++++++---- 2 files changed, 150 insertions(+), 41 deletions(-) diff --git a/client/src/components/Logins/EmailLogin.tsx b/client/src/components/Logins/EmailLogin.tsx index 10bc2df..f34c5ac 100644 --- a/client/src/components/Logins/EmailLogin.tsx +++ b/client/src/components/Logins/EmailLogin.tsx @@ -6,7 +6,7 @@ import { setLoginState } from "../../reducer/member/loginSlice"; import { setLogoutState } from "../../reducer/member/loginSlice"; import { useDispatch } from "react-redux"; -const EmailLoginModal: React.FC = ({ onClose, onLogin, onSignup }) => { +const EmailLoginModal: React.FC = ({ onClose, onSignup }) => { const titleText = "이메일로 로그인"; const emailLabelText = "이메일"; const passwordLabelText = "비밀번호"; @@ -43,7 +43,7 @@ const EmailLoginModal: React.FC = ({ onClose, onLogin, onS } }; - // 🔴 자옹 로그아웃 테스트 + // 🔴 자동 로그아웃 관련 코드 -> 정리 필요 const handleLoginClick = async () => { try { const response = await axios.post("http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/members/login", { email, password }, { validateStatus: (status) => status >= 200 && status < 600 }); @@ -52,9 +52,9 @@ const EmailLoginModal: React.FC = ({ onClose, onLogin, onS const accessToken = response.headers["authorization"]; const refreshToken = response.headers["refresh"]; - dispatch(setLoginState()); if (accessToken) sessionStorage.setItem("accessToken", accessToken); if (refreshToken) sessionStorage.setItem("refreshToken", refreshToken); + dispatch(setLoginState()); const toastStyle = { fontSize: "15px", @@ -63,15 +63,15 @@ const EmailLoginModal: React.FC = ({ onClose, onLogin, onS }; // 로그인 유지시긴 알림 - toast.warning("로그인 상태는 30분 동안 지속됩니다", { + toast.warning("로그인 상태는 30분 동안 유지됩니다", { style: toastStyle, position: "top-center", }); // 로그아웃 알림 1차 설정 + 이때 시간 저장 - const settingTime01 = 1000 * 7; // 10초 - const settingTime02 = 1000 * 7; // 10초 - const logoutAlarmTime01 = Date.now(); // 소환한 시간 + const settingTime01 = 1000 * 60 * 29; // 29분 + const settingTime02 = 1000 * 60; // 1분 + const logoutAlarmTime01 = Date.now(); // 1차 알람 호출한 시간 sessionStorage.setItem("logoutAlarmTime01", `${logoutAlarmTime01}`); // 세션 스토리지에 저장 setTimeout(() => { @@ -84,7 +84,7 @@ const EmailLoginModal: React.FC = ({ onClose, onLogin, onS }); // 2차 알람 및 로그아웃 처리 + 토큰 삭제 - const logoutAlarmTime02 = Date.now(); + const logoutAlarmTime02 = Date.now(); // 2차 알람 호출한 시간 sessionStorage.setItem("logoutAlarmTime02", `${logoutAlarmTime02}`); setTimeout(() => { @@ -102,7 +102,6 @@ const EmailLoginModal: React.FC = ({ onClose, onLogin, onS }, settingTime02); }, settingTime01); - // onLogin(); onClose(); } else { setGeneralError(response.data.message || JSON.stringify(response.data)); @@ -115,6 +114,7 @@ const EmailLoginModal: React.FC = ({ onClose, onLogin, onS } } }; + // 🔴 자동 로그아웃 관련 코드 -> 정리 필요 return ( @@ -141,7 +141,6 @@ export default EmailLoginModal; // 컴포넌트 props 타입 정의 interface EmailLoginModalProps { onClose: () => void; - onLogin: () => void; onSignup: () => void; } diff --git a/client/src/page/MainPage.tsx b/client/src/page/MainPage.tsx index 144ccb7..536c9cc 100644 --- a/client/src/page/MainPage.tsx +++ b/client/src/page/MainPage.tsx @@ -125,12 +125,6 @@ const MainPage = () => { const [isLoginConfirmationModalOpen, setLoginConfirmationModalOpen] = useState(false); - const handleLogin = () => { - closeEmailLoginModal(); - setLoginConfirmationModalOpen(true); - dispatch(setLoginState()); - }; - const handleLoginConfirmationClose = () => { setLoginConfirmationModalOpen(false); }; @@ -143,7 +137,7 @@ const MainPage = () => { setSelectedMenu(menu); }; - // 🔴🔴 페이지 로드 시 로컬 스토리지의 토큰을 기반으로 로그인 상태를 확인합니다. + // 🔴 자동 로그아웃 관련 코드 -> 정리 필요 useEffect(() => { const acessToken = sessionStorage.getItem("accessToken"); if (acessToken !== null) { @@ -158,11 +152,14 @@ const MainPage = () => { // 1) 현재시간 const currentTime = Date.now(); - const settingTime01 = 1000 * 7; // 10초 - const settingTime02 = 1000 * 7; // 10초 + const settingTime01 = 1000 * 60 * 29; // 29분 + const settingTime02 = 1000 * 60; // 1분 - // 2) 첫번째 알람 타이머가 아직 있다면 + // 로그인 알람 설정한 시간 (세선 스토리지에 저장 되어있음) const logoutAlarmTime01 = sessionStorage.getItem("logoutAlarmTime01"); + const logoutAlarmTime02 = sessionStorage.getItem("logoutAlarmTime02"); + + // 2) 첫번째 알람 타이머가 아직 있다면 if (logoutAlarmTime01 !== null) { // 3) 비동기 설정 시간 - 새로고침 전까지 지나간 시간 const timeGone = currentTime - parseInt(logoutAlarmTime01); @@ -177,7 +174,7 @@ const MainPage = () => { position: "top-center", }); - // 2차 알람 및 로그아웃 처리 + 토큰 삭제 + // 2차 알람 세팅 const logoutAlarmTime02 = Date.now(); sessionStorage.setItem("logoutAlarmTime02", `${logoutAlarmTime02}`); @@ -193,50 +190,163 @@ const MainPage = () => { style: toastStyle, position: "top-center", }); - }, 7000); + }, settingTime02); }, remainTime); + } - // 3) 두번째 알람 타이머가 아직 있다면 - const logoutAlarmTime02 = sessionStorage.getItem("logoutAlarmTime02"); - if (logoutAlarmTime02 !== null) { - const timeGone = currentTime - parseInt(logoutAlarmTime02); - const remainTime = settingTime02 - timeGone; + // 3) 첫번째 타이머 실행 후 -> 두번째 타이머 설정했는데 새로고침 시 + if (logoutAlarmTime02 !== null) { + const timeGone = currentTime - parseInt(logoutAlarmTime02); + const remainTime = settingTime02 - timeGone; - setTimeout(() => { - // 두번째 알람 실행되었으므로 -> 두번째 시간기록 삭제 - sessionStorage.removeItem("logoutAlarmTime02"); + setTimeout(() => { + // 두번째 알람 실행되었으므로 -> 두번째 시간기록 삭제 + sessionStorage.removeItem("logoutAlarmTime02"); - dispatch(setLogoutState()); - sessionStorage.removeItem("accessToken"); - sessionStorage.removeItem("refreshToken"); + dispatch(setLogoutState()); + sessionStorage.removeItem("accessToken"); + sessionStorage.removeItem("refreshToken"); - toast.warning("로그아웃 처리되었습니다", { - style: toastStyle, - position: "top-center", - }); - }, remainTime); - } + toast.warning("로그아웃 처리되었습니다", { + style: toastStyle, + position: "top-center", + }); + }, remainTime); } } }, []); + // Oauth 로그인 관련 코드 useEffect(() => { + // MainPage로 돌아왔을 때 url에 prameter가 있다면 -> url을 따서 const urlParams = new URLSearchParams(window.location.search); const accessToken = urlParams.get("access_token"); const refreshToken = urlParams.get("refresh_token"); + // 세션 스토리지에 저장 + 로그인 처리를 한다 if (accessToken && refreshToken) { sessionStorage.setItem("accessToken", `Bearer ${accessToken}`); sessionStorage.setItem("refreshToken", refreshToken); dispatch(setLoginState()); - // Remove access_token and refresh_token from the URL + + // url에 있는 파라미터를 지운다 urlParams.delete("access_token"); urlParams.delete("refresh_token"); window.history.replaceState({}, "", "?" + urlParams.toString()); window.location.reload(); + + // 🔴 자동 로그아웃 테스트 + // 현재 시간, 알림 세팅 타임, 세션 스토리지에 저장된 타이머 시간 + const currentTime = Date.now(); + const settingTime01 = 1000 * 60 * 29; // 29분 + const settingTime02 = 1000 * 60; // 1분 + const logoutAlarmTime01 = sessionStorage.getItem("logoutAlarmTime01"); + const logoutAlarmTime02 = sessionStorage.getItem("logoutAlarmTime02"); + + const toastStyle = { + fontSize: "15px", + fontWeight: 350, + color: "black", + }; + + // ✅ 1차 타이머도 설정되지 않았다면 -> 최초 설정 시 + if (logoutAlarmTime01 === null) { + toast.warning("로그인 상태는 30분 동안 유지됩니다", { + style: toastStyle, + position: "top-center", + }); + + // 1차 타이머 저장 + const logoutAlarmTime01 = Date.now(); // 1차 알람 호출한 시간 + sessionStorage.setItem("logoutAlarmTime01", `${logoutAlarmTime01}`); // 세션 스토리지에 저장 + + setTimeout(() => { + // 첫번째 알람 실행되었으므로 -> 첫번째 시간기록 삭제 + sessionStorage.removeItem("logoutAlarmTime01"); + + toast.warning("1분 뒤 로그아웃 처리됩니다", { + style: toastStyle, + position: "top-center", + }); + + // 2차 알람 및 로그아웃 처리 + 토큰 삭제 + const logoutAlarmTime02 = Date.now(); // 2차 알람 호출한 시간 + sessionStorage.setItem("logoutAlarmTime02", `${logoutAlarmTime02}`); + + setTimeout(() => { + // 두번째 알람 실행되었으므로 -> 두번째 시간기록 삭제 + sessionStorage.removeItem("logoutAlarmTime02"); + + dispatch(setLogoutState()); + sessionStorage.removeItem("accessToken"); + sessionStorage.removeItem("refreshToken"); + + toast.warning("로그아웃 처리되었습니다", { + style: toastStyle, + position: "top-center", + }); + }, settingTime02); + }, settingTime01); + } + + // ✅ 1차 타이머는 설정 됐는데 -> 새로고침 시 + if (logoutAlarmTime01 !== null) { + // 3) 비동기 설정 시간 - 새로고침 전까지 지나간 시간 + const timeGone = currentTime - parseInt(logoutAlarmTime01); + const remainTime = settingTime01 - timeGone; + + setTimeout(() => { + // 첫번째 알람 실행되었으므로 -> 첫번째 시간기록 삭제 + sessionStorage.removeItem("logoutAlarmTime01"); + + toast.warning("1분 뒤 로그아웃 처리됩니다", { + style: toastStyle, + position: "top-center", + }); + + // 2차 알람설정 + const logoutAlarmTime02 = Date.now(); + sessionStorage.setItem("logoutAlarmTime02", `${logoutAlarmTime02}`); + + setTimeout(() => { + // 두번째 알람 실행되었으므로 -> 두번째 시간기록 삭제 + sessionStorage.removeItem("logoutAlarmTime02"); + + dispatch(setLogoutState()); + sessionStorage.removeItem("accessToken"); + sessionStorage.removeItem("refreshToken"); + + toast.warning("로그아웃 처리되었습니다", { + style: toastStyle, + position: "top-center", + }); + }, settingTime02); + }, remainTime); + } + + // ✅ 두번째 타이머 설정 됐는데 -> 새로고침 시 + if (logoutAlarmTime02 !== null) { + const timeGone = currentTime - parseInt(logoutAlarmTime02); + const remainTime = settingTime02 - timeGone; + + setTimeout(() => { + // 두번째 알람 실행되었으므로 -> 두번째 시간기록 삭제 + sessionStorage.removeItem("logoutAlarmTime02"); + + dispatch(setLogoutState()); + sessionStorage.removeItem("accessToken"); + sessionStorage.removeItem("refreshToken"); + + toast.warning("로그아웃 처리되었습니다", { + style: toastStyle, + position: "top-center", + }); + }, remainTime); + } } }, []); + // 🔴 자동 로그아웃 관련 코드 -> 정리 필요 const [isGuideModalOpen, setGuideModalOpen] = useState(false); @@ -263,7 +373,7 @@ const MainPage = () => { handleMenuChange("관심종목")} onHoldingsClick={() => handleMenuChange("보유종목")} /> )} - {isEmailLoginModalOpen && } + {isEmailLoginModalOpen && } {isLoginConfirmationModalOpen && } {isEmailSignupModalOpen && }