
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package com.takensoft.common.util;
import com.takensoft.cms.mber.vo.MberAuthorVO;
import com.takensoft.cms.mber.vo.MberVO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import jakarta.servlet.http.Cookie;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @author : takensoft
* @since : 2025.01.22
* @modification
* since | author | description
* 2025.01.22 | takensoft | 최초 등록
*
* JWT 토큰 생성 및 검증, 쿠키 생성 등의 유틸리티
*/
@Component
public class JWTUtil {
private static SecretKey JWT_SECRET_KEY;
/**
* @param secret - JWT 서명을 위한 키 (application.yml에서 값을 읽어 옴)
*
* 기본 생성자
*/
public JWTUtil(@Value("${jwt.secret}")String secret) {
this.JWT_SECRET_KEY = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm());
}
/**
* @param category 토큰의 카테고리 정보 (Authorization, refresh)
* @param mbrId 사용자 ID
* @param lgnId 로그인 ID
* @param mbrNm 사용자 이름
* @param roles 사용자 권한 목록
* @param expiredMs 토큰 만료 시간 (밀리초)
* @return 생성된 JWT 토큰 (String)
*
* JWT 토큰 생성
*/
public String createJwt(String category, String mbrId, String lgnId, String mbrNm, List<String> roles, long expiredMs) {
return Jwts.builder()
.claim("category", category)
.claim("mbrId", mbrId)
.claim("lgnId", lgnId)
.claim("mbrNm", mbrNm)
.claim("roles", roles)
.issuedAt(new Date(System.currentTimeMillis())) // 토큰 발행 시간
.expiration(new Date(System.currentTimeMillis() + expiredMs)) // 토큰 소멸 시간
.signWith(JWT_SECRET_KEY)
.compact();
}
/**
* @param key 쿠키 키 값
* @param value 쿠키 값
* @param time 쿠키의 생명주기 (초 단위)
* @return 생성된 Cookie 객체
*
* 쿠키 생성
*/
public Cookie createCookie(String key, String value, int time) {
// 쿠키 생성
Cookie cookie = new Cookie(key, value);
cookie.setMaxAge(time); // 생명주기
//cookie.setSecure(true); // https 통신을 할 경우 true로 사용
cookie.setPath("/"); // 쿠키 적용 범위
cookie.setHttpOnly(true); // front에서 script로 접근 방지
return cookie;
}
/**
* @param tkn JWT 토큰 문자열
* @param knd 조회할 데이터의 종류 (예: ctgry, userId, lgnId 등)
* @return 조회된 클레임 데이터 (종류에 따라 String, Date, List 등으로 반환)
* @throws IllegalArgumentException 유효하지 않은 knd 값일 경우 예외 발생
*
* 클레임 조회
*/
public Object getClaim(String tkn, String knd) {
Claims claims;
try {
// 토큰 값 검증
if (tkn == null || tkn.trim().isEmpty()) {
throw new IllegalArgumentException("Token is null or empty");
}else{
tkn = extractToken(tkn);
}
claims = Jwts.parser()
.verifyWith(JWT_SECRET_KEY)
.build()
.parseSignedClaims(tkn)
.getPayload();
} catch (ExpiredJwtException e) {
// 만료된 토큰이라도 claims 꺼내기 가능
claims = e.getClaims();
} catch (JwtException | IllegalArgumentException e) {
// 토큰 자체가 잘못된 경우
throw new IllegalArgumentException("Invalid token");
}
switch (knd) {
case "category":
return claims.get("category", String.class);
case "mbrId":
return claims.get("mbrId", String.class);
case "lgnId":
return claims.get("lgnId", String.class);
case "mbrNm":
return claims.get("mbrNm", String.class);
case "roles":
List<Map<String, Object>> roles = claims.get("roles", List.class);
List<MberAuthorVO> authorList = new ArrayList<>();
if (roles != null && !roles.isEmpty()) {
for (Map<String, Object> role : roles) {
MberAuthorVO userAuthor = new MberAuthorVO(role.get("authority").toString());
authorList.add(userAuthor);
}
}
return authorList;
case "isExpired":
return claims.getExpiration().before(new Date());
case "expired":
return claims.getExpiration();
default:
throw new IllegalArgumentException("Invalid knd : " + knd);
}
}
/**
* @param authHeader JWT 토큰 문자열
* @return 실제 토큰 부분만 추출
*
* Bearer 토큰에서 실제 토큰 부분만 추출하는 메서드
*/
public String extractToken(String authHeader) {
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
return authHeader; // Bearer가 없으면 그대로 반환
}
}