ERP-node/backend-node/src/services/authService.ts

349 lines
10 KiB
TypeScript

// 인증 서비스
// 기존 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<LoginResult> {
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<void> {
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<PersonBean | null> {
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;
}
// 권한 정보 조회 (Prisma ORM 사용)
const authInfo = await prisma.authority_sub_user.findMany({
where: {
user_id: userId,
},
include: {
authority_master: {
select: {
auth_name: true,
},
},
},
});
// 권한명들을 쉼표로 연결
const authNames = authInfo
.filter((auth: any) => auth.authority_master?.auth_name)
.map((auth: any) => auth.authority_master!.auth_name!)
.join(",");
// 회사 정보 조회 (Prisma ORM 사용으로 변경)
const companyInfo = await prisma.company_mng.findFirst({
where: {
company_code: userInfo.company_code || "ILSHIN",
},
select: {
company_name: true,
},
});
// DB에서 조회한 원본 사용자 정보 상세 로그
console.log("🔍 AuthService - DB 원본 사용자 정보:", {
userId: userInfo.user_id,
company_code: userInfo.company_code,
company_code_type: typeof userInfo.company_code,
company_code_is_null: userInfo.company_code === null,
company_code_is_undefined: userInfo.company_code === undefined,
company_code_is_empty: userInfo.company_code === "",
dept_code: userInfo.dept_code,
allUserFields: Object.keys(userInfo),
companyInfo: companyInfo?.company_name,
});
// 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: authNames || undefined,
companyCode: userInfo.company_code || "ILSHIN",
photo: userInfo.photo
? `data:image/jpeg;base64,${userInfo.photo.toString("base64")}`
: undefined,
locale: userInfo.locale || "KR",
};
console.log("📦 AuthService - 최종 PersonBean:", {
userId: personBean.userId,
companyCode: personBean.companyCode,
deptCode: personBean.deptCode,
});
logger.info(`사용자 정보 조회 완료: ${userId}`);
return personBean;
} catch (error) {
logger.error(
`사용자 정보 조회 중 오류 발생: ${error instanceof Error ? error.message : error}`
);
return null;
}
}
/**
* JWT 토큰으로 사용자 정보 조회
*/
static async getUserInfoFromToken(token: string): Promise<PersonBean | null> {
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<void> {
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}`
);
}
}
}