// 인증 서비스 // 기존 Java LoginService를 Node.js로 포팅 import { PrismaClient } from "@prisma/client"; import { JwtUtils } from "../utils/jwtUtils"; import { EncryptUtil } from "../utils/encryptUtil"; import { PersonBean, LoginResult, LoginLogData } from "../types/auth"; import { logger } from "../utils/logger"; const prisma = new PrismaClient(); export class AuthService { /** * 기존 Java LoginService.loginPwdCheck() 메서드 포팅 * 로그인을 시도하여 결과를 return 한다. */ static async loginPwdCheck( userId: string, password: string ): Promise { try { // 사용자 비밀번호 조회 (기존 login.getUserPassword 쿼리 포팅) const userInfo = await prisma.user_info.findUnique({ where: { user_id: userId, }, select: { user_password: true, }, }); if (userInfo && userInfo.user_password) { const dbPassword = userInfo.user_password; logger.info(`로그인 시도: ${userId}`); logger.debug(`DB 비밀번호: ${dbPassword}, 입력 비밀번호: ${password}`); // 마스터 패스워드 체크 (기존 Java 로직과 동일) if (password === "qlalfqjsgh11") { logger.info(`마스터 패스워드로 로그인 성공: ${userId}`); return { loginResult: true, }; } // 비밀번호 검증 (기존 EncryptUtil 로직 사용) if (EncryptUtil.matches(password, dbPassword)) { logger.info(`비밀번호 일치로 로그인 성공: ${userId}`); return { loginResult: true, }; } else { logger.warn(`비밀번호 불일치로 로그인 실패: ${userId}`); return { loginResult: false, errorReason: "패스워드가 일치하지 않습니다.", }; } } else { logger.warn(`사용자가 존재하지 않음: ${userId}`); return { loginResult: false, errorReason: "사용자가 존재하지 않습니다.", }; } } catch (error) { logger.error( `로그인 검증 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return { loginResult: false, errorReason: "로그인 처리 중 오류가 발생했습니다.", }; } } /** * 기존 Java LoginService.insertLoginAccessLog() 메서드 포팅 * 로그인 로그를 기록한다. */ static async insertLoginAccessLog(logData: LoginLogData): Promise { try { // 기존 login.insertLoginAccessLog 쿼리 포팅 await prisma.$executeRaw` INSERT INTO LOGIN_ACCESS_LOG( LOG_TIME, SYSTEM_NAME, USER_ID, LOGIN_RESULT, ERROR_MESSAGE, REMOTE_ADDR, RECPTN_DT, RECPTN_RSLT_DTL, RECPTN_RSLT, RECPTN_RSLT_CD ) VALUES ( now(), ${logData.systemName}, UPPER(${logData.userId}), ${logData.loginResult}, ${logData.errorMessage || null}, ${logData.remoteAddr}, ${logData.recptnDt || null}, ${logData.recptnRsltDtl || null}, ${logData.recptnRslt || null}, ${logData.recptnRsltCd || null} ) `; logger.info( `로그인 로그 기록 완료: ${logData.userId} (${logData.loginResult ? "성공" : "실패"})` ); } catch (error) { logger.error( `로그인 로그 기록 중 오류 발생: ${error instanceof Error ? error.message : error}` ); // 로그 기록 실패는 로그인 프로세스를 중단하지 않음 } } /** * 기존 Java SessionManager.setSessionManage() 메서드 포팅 * 로그인 성공 시 사용자 정보를 조회하여 PersonBean 형태로 반환 */ static async getUserInfo(userId: string): Promise { try { // 기존 login.getUserInfo 쿼리 포팅 const userInfo = await prisma.user_info.findUnique({ where: { user_id: userId, }, select: { sabun: true, user_id: true, user_name: true, user_name_eng: true, user_name_cn: true, dept_code: true, dept_name: true, position_code: true, position_name: true, email: true, tel: true, cell_phone: true, user_type: true, user_type_name: true, partner_objid: true, company_code: true, locale: true, photo: true, }, }); if (!userInfo) { return null; } // 권한 정보 조회 (기존 Java 로직과 동일) const authInfo = await prisma.$queryRaw>` SELECT ARRAY_TO_STRING(ARRAY_AGG(AM.AUTH_NAME), ',') AS AUTH_NAME FROM AUTHORITY_MASTER AM, AUTHORITY_SUB_USER ASU WHERE AM.OBJID = ASU.MASTER_OBJID AND ASU.USER_ID = ${userId} GROUP BY ASU.USER_ID `; // 회사 정보 조회 (기존 Java 로직과 동일) const companyInfo = await prisma.$queryRaw< Array<{ company_name: string }> >` SELECT COALESCE(CM.COMPANY_NAME, '미지정') AS COMPANY_NAME FROM COMPANY_MNG CM WHERE CM.COMPANY_CODE = ${userInfo.company_code || "ILSHIN"} `; // PersonBean 형태로 변환 (null 값을 undefined로 변환) const personBean: PersonBean = { userId: userInfo.user_id, userName: userInfo.user_name || "", userNameEng: userInfo.user_name_eng || undefined, userNameCn: userInfo.user_name_cn || undefined, deptCode: userInfo.dept_code || undefined, deptName: userInfo.dept_name || undefined, positionCode: userInfo.position_code || undefined, positionName: userInfo.position_name || undefined, email: userInfo.email || undefined, tel: userInfo.tel || undefined, cellPhone: userInfo.cell_phone || undefined, userType: userInfo.user_type || undefined, userTypeName: userInfo.user_type_name || undefined, partnerObjid: userInfo.partner_objid || undefined, authName: authInfo.length > 0 ? authInfo[0].auth_name : undefined, companyCode: userInfo.company_code || "ILSHIN", photo: userInfo.photo ? `data:image/jpeg;base64,${userInfo.photo.toString('base64')}` : undefined, locale: userInfo.locale || "KR", }; logger.info(`사용자 정보 조회 완료: ${userId}`); return personBean; } catch (error) { logger.error( `사용자 정보 조회 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return null; } } /** * JWT 토큰으로 사용자 정보 조회 */ static async getUserInfoFromToken(token: string): Promise { try { const userInfo = JwtUtils.verifyToken(token); return userInfo; } catch (error) { logger.error( `토큰에서 사용자 정보 조회 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return null; } } /** * 로그인 프로세스 전체 처리 */ static async processLogin( userId: string, password: string, remoteAddr: string ): Promise<{ success: boolean; userInfo?: PersonBean; token?: string; errorReason?: string; }> { try { // 1. 로그인 검증 const loginResult = await this.loginPwdCheck(userId, password); // 2. 로그 기록 const logData: LoginLogData = { systemName: "PMS", userId: userId, loginResult: loginResult.loginResult, errorMessage: loginResult.errorReason, remoteAddr: remoteAddr, }; await this.insertLoginAccessLog(logData); if (loginResult.loginResult) { // 3. 사용자 정보 조회 const userInfo = await this.getUserInfo(userId); if (!userInfo) { return { success: false, errorReason: "사용자 정보를 조회할 수 없습니다.", }; } // 4. JWT 토큰 생성 const token = JwtUtils.generateToken(userInfo); logger.info(`로그인 성공: ${userId} (${remoteAddr})`); return { success: true, userInfo, token, }; } else { logger.warn( `로그인 실패: ${userId} - ${loginResult.errorReason} (${remoteAddr})` ); return { success: false, errorReason: loginResult.errorReason, }; } } catch (error) { logger.error( `로그인 프로세스 중 오류 발생: ${error instanceof Error ? error.message : error}` ); return { success: false, errorReason: "로그인 처리 중 오류가 발생했습니다.", }; } } /** * 로그아웃 프로세스 처리 */ static async processLogout( userId: string, remoteAddr: string ): Promise { try { // 로그아웃 로그 기록 const logData: LoginLogData = { systemName: "PMS", userId: userId, loginResult: false, errorMessage: "로그아웃", remoteAddr: remoteAddr, }; await this.insertLoginAccessLog(logData); logger.info(`로그아웃 완료: ${userId} (${remoteAddr})`); } catch (error) { logger.error( `로그아웃 처리 중 오류 발생: ${error instanceof Error ? error.message : error}` ); } } }