// JWT 토큰 관리 유틸리티 // 기존 PersonBean 정보를 JWT 페이로드로 변환 import jwt from "jsonwebtoken"; import { PersonBean, JwtPayload } from "../types/auth"; import config from "../config/environment"; export class JwtUtils { /** * 사용자 정보로 JWT 토큰 생성 * 기존 PersonBean 정보를 JWT 페이로드로 변환 */ static generateToken(userInfo: PersonBean): string { try { const payload: JwtPayload = { userId: userInfo.userId, userName: userInfo.userName, deptName: userInfo.deptName, companyCode: userInfo.companyCode, userType: userInfo.userType, userTypeName: userInfo.userTypeName, }; return jwt.sign(payload, config.jwt.secret, { expiresIn: config.jwt.expiresIn, issuer: "PMS-System", audience: "PMS-Users", } as any); } catch (error) { console.error("JWT token generation error:", error); throw new Error("토큰 생성 중 오류가 발생했습니다."); } } /** * JWT 토큰 검증 및 사용자 정보 추출 */ static verifyToken(token: string): PersonBean { try { const decoded = jwt.verify(token, config.jwt.secret) as JwtPayload; // PersonBean 형태로 변환 const personBean: PersonBean = { userId: decoded.userId, userName: decoded.userName, deptName: decoded.deptName, companyCode: decoded.companyCode, userType: decoded.userType, userTypeName: decoded.userTypeName, }; return personBean; } catch (error) { if (error instanceof jwt.TokenExpiredError) { throw new Error("토큰이 만료되었습니다."); } else if (error instanceof jwt.JsonWebTokenError) { throw new Error("유효하지 않은 토큰입니다."); } else { console.error("JWT token verification error:", error); throw new Error("토큰 검증 중 오류가 발생했습니다."); } } } /** * JWT 토큰에서 페이로드만 추출 (검증 없이) */ static decodeToken(token: string): JwtPayload | null { try { return jwt.decode(token) as JwtPayload; } catch (error) { console.error("JWT token decode error:", error); return null; } } /** * 토큰 만료 시간 확인 */ static isTokenExpired(token: string): boolean { try { const decoded = jwt.decode(token) as JwtPayload; if (!decoded || !decoded.exp) { return true; } const currentTime = Math.floor(Date.now() / 1000); return decoded.exp < currentTime; } catch (error) { console.error("Token expiration check error:", error); return true; } } /** * 토큰 갱신 (만료 시간만 연장) */ static refreshToken(token: string): string { try { const decoded = jwt.decode(token) as JwtPayload; if (!decoded) { throw new Error("토큰을 디코드할 수 없습니다."); } // 페이로드에서 만료 시간 관련 필드 제거 const { iat, exp, aud, iss, ...payload } = decoded; return jwt.sign(payload, config.jwt.secret, { expiresIn: config.jwt.expiresIn, issuer: "PMS-System", } as any); } catch (error) { console.error("JWT token refresh error:", error); throw new Error("토큰 갱신 중 오류가 발생했습니다."); } } /** * 토큰에서 사용자 ID 추출 */ static getUserIdFromToken(token: string): string | null { try { const decoded = jwt.decode(token) as JwtPayload; return decoded?.userId || null; } catch (error) { console.error("Get user ID from token error:", error); return null; } } /** * 토큰 유효성 검사 (만료 여부 포함) */ static validateToken(token: string): { isValid: boolean; error?: string } { try { jwt.verify(token, config.jwt.secret); return { isValid: true }; } catch (error) { if (error instanceof jwt.TokenExpiredError) { return { isValid: false, error: "토큰이 만료되었습니다." }; } else if (error instanceof jwt.JsonWebTokenError) { return { isValid: false, error: "유효하지 않은 토큰입니다." }; } else { return { isValid: false, error: "토큰 검증 중 오류가 발생했습니다." }; } } } /** * 테스트용 메서드 (개발 환경에서만 사용) */ static testJwtUtils(userInfo: PersonBean): void { console.log("=== JWT 토큰 테스트 ==="); console.log("사용자 정보:", userInfo); const token = this.generateToken(userInfo); console.log("생성된 토큰:", token); const decoded = this.decodeToken(token); console.log("디코드된 페이로드:", decoded); const verified = this.verifyToken(token); console.log("검증된 사용자 정보:", verified); const isExpired = this.isTokenExpired(token); console.log("토큰 만료 여부:", isExpired); const userId = this.getUserIdFromToken(token); console.log("토큰에서 추출한 사용자 ID:", userId); const validation = this.validateToken(token); console.log("토큰 유효성 검사:", validation); } }