From 1dbfdad1b77764039b49d3e0e6622e3afb998a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=84=9D=20=EC=B5=9C?= Date: Tue, 19 Dec 2023 15:43:36 +0900 Subject: [PATCH] =?UTF-8?q?refreshToken=20=EC=83=9D=EC=84=B1=EB=B0=A9?= =?UTF-8?q?=EB=B2=95=20=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/egovFetch.js | 2 ++ .../src/components/EgovHeader.jsx | 8 +++++--- .../src/pages/login/EgovLoginContent.jsx | 10 +++++----- .../src/pages/standardCode/viewer.js | 3 ++- .../src/utils/parseJwt.jsx | 14 ++++++++++++++ .../kcscbackend/config/jwt/EgovJwtTokenUtil.java | 14 ++++++++------ .../kcscbackend/config/jwt/redis/RefreshToken.java | 2 +- .../CustomUrlAuthenticationSuccessHandler.java | 2 -- 8 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 egovframe-template-simple-react-contribution/src/utils/parseJwt.jsx diff --git a/egovframe-template-simple-react-contribution/src/api/egovFetch.js b/egovframe-template-simple-react-contribution/src/api/egovFetch.js index 07fecf0..2bf0b80 100644 --- a/egovframe-template-simple-react-contribution/src/api/egovFetch.js +++ b/egovframe-template-simple-react-contribution/src/api/egovFetch.js @@ -4,6 +4,7 @@ import URL from 'constants/url'; import CODE from 'constants/code'; import { getSessionItem, setSessionItem } from 'utils/storage'; import { getLocalItem, setLocalItem } from 'utils/storage'; +import {parseJwt} from "../utils/parseJwt"; export function getQueryString(params){ return `?${Object.entries(params).map(e => e.join('=')).join('&') }` @@ -18,6 +19,7 @@ export function requestFetch(url, requestOptions, handler, errorHandler) { const sessionUser = getLocalItem('loginUser'); const sessionUserId = sessionUser?.userId || null; const jToken = getLocalItem('jToken'); + const userInfo = parseJwt(jToken); const refreshToken = getLocalItem('refreshToken'); if(sessionUserId != null && sessionUserId !== undefined){ if( !requestOptions['headers'] ) requestOptions['headers']={} diff --git a/egovframe-template-simple-react-contribution/src/components/EgovHeader.jsx b/egovframe-template-simple-react-contribution/src/components/EgovHeader.jsx index 9b198a6..5cf7673 100644 --- a/egovframe-template-simple-react-contribution/src/components/EgovHeader.jsx +++ b/egovframe-template-simple-react-contribution/src/components/EgovHeader.jsx @@ -7,14 +7,16 @@ import URL from 'constants/url'; import CODE from 'constants/code'; import { getSessionItem, setSessionItem } from 'utils/storage'; import { getLocalItem, setLocalItem } from 'utils/storage'; +import {parseJwt} from "../utils/parseJwt"; function EgovHeader({ loginUser, onChangeLogin }) { console.group("EgovHeader"); console.log("[Start] EgovHeader ------------------------------"); - const sessionUser = getLocalItem('loginUser'); - const sessionUserId = sessionUser?.userId; - const sessionUserSe = sessionUser?.userSe; + const jToken = getLocalItem('jToken'); + const userInfo = parseJwt(jToken); + const sessionUserId = userInfo?.id; + const sessionUserSe = userInfo?.userSe; const navigate = useNavigate(); diff --git a/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx b/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx index 532e828..e254726 100644 --- a/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx +++ b/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import {Link, useLocation, useNavigate} from 'react-router-dom'; import * as EgovNet from 'api/egovFetch'; +import {parseJwt} from "../../utils/parseJwt"; import URL from 'constants/url'; import CODE from 'constants/code'; @@ -20,7 +21,6 @@ function EgovLoginContent(props) { const [userInfo, setUserInfo] = useState({ username: '', password: 'default', userSe: 'USR' }); // eslint-disable-next-line no-unused-vars - const [loginVO, setLoginVO] = useState({}); const [saveIDFlag, setSaveIDFlag] = useState(false); @@ -74,8 +74,8 @@ function EgovLoginContent(props) { EgovNet.requestFetch(loginUrl, requestOptions, (resp) => { - let resultVO = resp.resultVO; let jToken = resp?.jToken || null; + let resultVO = parseJwt(jToken); let refreshToken = resp?.refreshToken || null; // setSessionItem('jToken', jToken); @@ -83,11 +83,11 @@ function EgovLoginContent(props) { setLocalItem('refreshToken', refreshToken); debugger if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) { - setLoginVO(resultVO); // setSessionItem('loginUser', resultVO); - setLocalItem('loginUser', resultVO); props.onChangeLogin(resultVO); - if (saveIDFlag) setLocalItem(KEY_ID, resultVO?.id); + if (saveIDFlag) { + setLocalItem(KEY_ID, resultVO?.id); + } navigate(URL.MAIN); // PC와 Mobile 열린메뉴 닫기 document.querySelector('.all_menu.WEB').classList.add('closed'); diff --git a/egovframe-template-simple-react-contribution/src/pages/standardCode/viewer.js b/egovframe-template-simple-react-contribution/src/pages/standardCode/viewer.js index 64e6c50..f8aeb09 100644 --- a/egovframe-template-simple-react-contribution/src/pages/standardCode/viewer.js +++ b/egovframe-template-simple-react-contribution/src/pages/standardCode/viewer.js @@ -12,6 +12,7 @@ import Modal from 'react-bootstrap/Modal'; import * as EgovNet from 'api/egovFetch'; import {getLocalItem} from "../../utils/storage"; import CODE from "../../constants/code"; +import {parseJwt} from "../../utils/parseJwt"; function CodeViewer(props) { const [treeLoading, setTreeLoading] = useState(true); @@ -29,7 +30,7 @@ function CodeViewer(props) { const [bookMarkModal, setBookMarkModal] = useState(); - const sessionUser = getLocalItem('loginUser'); + const sessionUser = parseJwt(getLocalItem('jToken')); const sessionUserSe = sessionUser?.userSe; const handleClose = () => setShow(false); diff --git a/egovframe-template-simple-react-contribution/src/utils/parseJwt.jsx b/egovframe-template-simple-react-contribution/src/utils/parseJwt.jsx new file mode 100644 index 0000000..dc8ad16 --- /dev/null +++ b/egovframe-template-simple-react-contribution/src/utils/parseJwt.jsx @@ -0,0 +1,14 @@ +const parseJwt = (token) => { + if(!token){ + return null; + } + var base64Url = token.split('.')[1]; + var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); + var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); + + return JSON.parse(jsonPayload); +}; + +export {parseJwt}; \ No newline at end of file diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java index 91f8699..44ad5f0 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java @@ -51,7 +51,8 @@ public class EgovJwtTokenUtil implements Serializable{ private static final long serialVersionUID = -5180902194184255251L; //public static final long JWT_TOKEN_VALIDITY = 24 * 60 * 60; //하루 - public static final long JWT_ACCESS_TOKEN_VALIDITY = (30); //토큰의 유효시간 설정, 기본 60분 60*60 + public static final long JWT_ACCESS_TOKEN_SEC = (30*60*1000); //엑세스 토큰의 유효시간 설정, 30분 + public static final long JWT_REFRESH_TOKEN_SEC = (60*60*24*14*1000); //리프레시 토큰의 유효시간 설정, 2주 public static final String SECRET_KEY = EgovProperties.getProperty("Globals.jwt.secret"); private final RefreshTokenRepository refreshTokenRepository; @@ -88,11 +89,10 @@ public class EgovJwtTokenUtil implements Serializable{ //2. Sign the JWT using the HS512 algorithm and secret key. //3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) // compaction of the JWT to a URL-safe string - public String generateAccessToken(UserInfo loginVO) { + public String generateToken(UserInfo loginVO, Long sec) { Map claims = new HashMap<>(); claims.put("id", loginVO.getUserId() ); claims.put("userSe", loginVO.getUserSe() ); - claims.put("uniqId", loginVO.getUserSeq() ); claims.put("type", "Authorization"); log.debug("===>>> secret = "+SECRET_KEY); @@ -101,14 +101,16 @@ public class EgovJwtTokenUtil implements Serializable{ .setClaims(claims) .setSubject("Authorization") .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + JWT_ACCESS_TOKEN_VALIDITY * 1000)) + .setExpiration(new Date(System.currentTimeMillis() + sec)) .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)), SignatureAlgorithm.HS512); return builder.compact(); } - + public String generateAccessToken(UserInfo loginVO) { + return generateToken(loginVO, JWT_ACCESS_TOKEN_SEC); + } @Transactional public String generateRefreshTokenToken(UserInfo loginVO){ - RefreshToken refreshToken = new RefreshToken(loginVO.getUserSeq(), UUID.randomUUID().toString()); + RefreshToken refreshToken = new RefreshToken(loginVO.getUserSeq(), generateToken(loginVO, JWT_REFRESH_TOKEN_SEC)); refreshTokenRepository.save(refreshToken); return refreshToken.getRefreshToken(); } diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshToken.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshToken.java index 3c7e53c..91fdab0 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshToken.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshToken.java @@ -10,7 +10,7 @@ import org.springframework.data.redis.core.index.Indexed; @Getter @AllArgsConstructor @NoArgsConstructor -@RedisHash(value="refreshToken", timeToLive = 60) // *60*24*14 +@RedisHash(value="refreshToken", timeToLive = 60*60*24*14) public class RefreshToken { @Id diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java index c11c916..b5ef287 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java @@ -58,10 +58,8 @@ public class CustomUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticati securityUser.setUserSeq(null); HashMap resultMap = new HashMap(); resultMap.put("resultCode", "200"); - resultMap.put("resultVO", (UserDetails)securityUser); resultMap.put("refreshToken", refreshToken); resultMap.put("jToken", jwtToken); - // String userName = jwtTokenUtil.getUserSeFromToken(jwtToken); response.addHeader("Authorization", "BEARER "+jwtToken); if (jsonConverter.canWrite(resultMap.getClass(), jsonMimeType)) { jsonConverter.write(resultMap, jsonMimeType, new ServletServerHttpResponse(response));