Merge remote-tracking branch 'origin/master'
eapim-admin CI / build (push) Has been cancelled

This commit is contained in:
Rinjae
2026-07-29 19:24:38 +09:00
13 changed files with 141 additions and 28 deletions
@@ -51,6 +51,9 @@ public class AuditLogPageRepositoryImpl implements AuditLogPageRepository {
if (StringUtils.isNotBlank(command.getSearchRemoteAddress())) {
jpaQuery.where(qAuditLogEntity.remoteAddress.containsIgnoreCase(command.getSearchRemoteAddress()));
}
if (StringUtils.isNotBlank(command.getSearchParameters())) {
jpaQuery.where(qAuditLogEntity.parameters.containsIgnoreCase(command.getSearchParameters()));
}
long totalCount = jpaQuery.fetchOne();
List<AuditLogEntity> auditLogs = jpaQuery
@@ -18,6 +18,7 @@ public class SelectListAuditLogCommand {
private String searchUserId;
private String searchRemoteAddress;
private String searchParameters;
@JsonFormat(pattern = "yyyyMMddHHmmss")
@DateTimeFormat(pattern = "yyyyMMddHHmmss")
@@ -41,8 +41,8 @@ import lombok.RequiredArgsConstructor;
@Transactional(transactionManager = "transactionManagerForEMS")
@RequiredArgsConstructor
public class UserManService extends BaseService {
private static final String USER_STATUS_NORMAL = "1";
private final UserInfoService userInfoService;
private final UserRoleService userRoleService;
@@ -100,7 +100,8 @@ public class UserManService extends BaseService {
UserInfo userInfo = userUIMapper.toEntity(userUI);
userInfo.setRoleidnfiname(CommonConstants.DEPT_DEVELOPER);
userInfo.setStatus(USER_STATUS_NORMAL);
String subfix = monitoringContext.getStringProperty(MonitoringContext.RMS_PASSWORD_INIT_SUBFIX, "@!");
try {
@@ -133,6 +134,12 @@ public class UserManService extends BaseService {
public void update(UserUI userUI) {
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
userUIMapper.updateToEntity(userUI, userInfo);
// 계정상태를 정상으로 저장하면 로그인 실패횟수 초기화
if (USER_STATUS_NORMAL.equals(userInfo.getStatus())) {
userInfo.setLoginfailcount(0);
}
userInfoService.save(userInfo);
}
@@ -148,6 +155,11 @@ public class UserManService extends BaseService {
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
userInfo.setPassword(DamoManager.getInstance().hash(DamoManager.SHA256, userUI.getUserId()+subfix));
userUIMapper.updateToEntity(userUI, userInfo);
// 비밀번호 초기화 시 계정상태를 정상으로 변경하고 로그인 실패횟수 초기화
userInfo.setLoginfailcount(0);
userInfo.setStatus(USER_STATUS_NORMAL);
userInfoService.save(userInfo);
}
@@ -265,6 +265,9 @@ public interface MonitoringContext {
//비밀번호 초기화 접미사
public static final String RMS_PASSWORD_INIT_SUBFIX = "rms.password.init.subfix";
// 최대 로그인 실패 허용횟수 (0이면 계정 잠금 미적용)
public static final String RMS_PASSWORD_FAIL_COUNT = "rms.password.fail.count";
@@ -64,6 +64,9 @@ public class MainController implements InterceptorSkipController {
private static final String REDIRECT_LOGIN_URL = "redirect:/emergency.jsp";
private static final String REDIRECT_CHANGE_PASSWORD_URL = "redirect:/emergency.jsp";
private static final String USER_STATUS_LOCKED = "2";
private static final int DEFAULT_MAX_LOGIN_FAIL_COUNT = 5;
private final LocaleMessage localeMessage;
private final MonitoringContext monitoringContext;
@@ -160,7 +163,7 @@ public class MainController implements InterceptorSkipController {
session.removeAttribute(RESULT_MSG);
return LoginResponseDto.builder()
.success(false)
.errorMessage(errorMsg != null ? errorMsg : "로그인에 실패했습니다.")
.errorMessage(toAlertMessage(errorMsg != null ? errorMsg : "로그인에 실패했습니다."))
.build();
}
@@ -345,10 +348,20 @@ public class MainController implements InterceptorSkipController {
//logger.info("사용자 계정 상태: " + userInfo.getStatus());
if ( userInfo.getStatus() != null && "2".equals(userInfo.getStatus()) ) {
setLoginFailure(request, session, "login.checkstatus1",
"login.checkstatus2");
return null;
if ( USER_STATUS_LOCKED.equals(userInfo.getStatus()) ) {
int maxLoginFailCount = getMaxLoginFailCount();
boolean lockedByFailCount = maxLoginFailCount > 0
&& userInfo.getLoginfailcount() != null
&& userInfo.getLoginfailcount() >= maxLoginFailCount;
if (lockedByFailCount) {
setLoginFailure(request, session, "login.accountlocked1",
"login.accountlocked2", String.valueOf(maxLoginFailCount));
} else {
setLoginFailure(request, session, "login.checkstatus1",
"login.checkstatus2");
}
return null;
}
//logger.info("사용자 ip: " + userInfo.getAllowip());
@@ -374,27 +387,29 @@ public class MainController implements InterceptorSkipController {
/* 기존로직 - LDAP 사용 x */
if (!userInfo.getPassword().equals(DamoManager.getInstance().hash(DamoManager.SHA256, dto.getPassword()))) {
// password 다름.
int failCount = increaseLoginFailCount(userInfo);
setLoginFailure(request, session, "login.loginfail1",
"login.loginfail2");
"login.loginfail2", failCount);
return null;
}
} else {
String password = dto.getPassword();
password = password.replace("&#35;","#");
password = password.replace("&#38;","&");
/* LDAP */
//if (!authenticate(userInfo.getUserid(), dto.getPassword())) {
if (!authenticate(userInfo.getUserid(), password)) {
// password 다름.
int failCount = increaseLoginFailCount(userInfo);
setLoginFailure(request, session, "login.loginfail1",
"login.loginfail2");
"login.loginfail2", failCount);
return null;
}
}
session = request.getSession(true);
session.setAttribute("userId", dto.getUserId());
@@ -432,7 +447,28 @@ public class MainController implements InterceptorSkipController {
return userInfo;
}
// LDAP
// 최대 로그인 실패 허용횟수 조회 (rms.password.fail.count, 0이면 계정 잠금 미적용)
private int getMaxLoginFailCount() {
return monitoringContext.getIntProperty(
MonitoringContext.RMS_PASSWORD_FAIL_COUNT, DEFAULT_MAX_LOGIN_FAIL_COUNT);
}
// 로그인 실패 횟수 증가, 임계치 도달 시 계정 잠금
private int increaseLoginFailCount(UserInfo userInfo) {
int failCount = (userInfo.getLoginfailcount() == null ? 0 : userInfo.getLoginfailcount()) + 1;
userInfo.setLoginfailcount(failCount);
int maxLoginFailCount = getMaxLoginFailCount();
if (maxLoginFailCount > 0 && failCount >= maxLoginFailCount) {
userInfo.setStatus(USER_STATUS_LOCKED);
logger.warn("로그인 " + maxLoginFailCount + "회 이상 실패로 계정 잠금 처리: " + userInfo.getUserid());
}
userInfoService.save(userInfo);
return failCount;
}
// LDAP
public boolean authenticate(String userId, String password) {
String LDAP_URL = monitoringPropertyService.getPrpty2ValById("Monitoring", "ldap.domain");
String NETBIOS = monitoringPropertyService.getPrpty2ValById("Monitoring", "ldap.netbios");
@@ -496,6 +532,7 @@ public class MainController implements InterceptorSkipController {
String lastLoginIp = IpUtil.getClientIp(request);
userInfo.setLastloginyms(LocalDateTime.now());
userInfo.setLastloginip(lastLoginIp);
userInfo.setLoginfailcount(0);
userInfoService.save(userInfo);
}
@@ -556,12 +593,32 @@ public class MainController implements InterceptorSkipController {
return "redirect:/common/errors/errorLogon.jsp";
}
// HTML 개행 태그(<BR/> 등)를 JS alert용 개행문자로 변환
private String toAlertMessage(String message) {
return message.replaceAll("(?i)<br\\s*/?>", "\n");
}
// 로그인 실패 시 로그 기록, 세션에 실패 메시지 설정
private void setLoginFailure(HttpServletRequest request,
HttpSession session, String logKey, String msgKey) {
HttpSession session, String logKey, String msgKey,
String... msgArgs) {
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
localeMessage.getString(logKey));
session.setAttribute(RESULT_MSG, localeMessage.getString(msgKey));
session.setAttribute(RESULT_MSG, localeMessage.getString(msgKey, msgArgs));
}
// 로그인 실패 시 로그 기록, 세션에 실패 메시지(실패 횟수 포함) 설정
private void setLoginFailure(HttpServletRequest request,
HttpSession session, String logKey, String msgKey,
int failCount) {
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
localeMessage.getString(logKey));
String msg = localeMessage.getString(msgKey);
int maxLoginFailCount = getMaxLoginFailCount();
if (maxLoginFailCount > 0) {
msg += " (" + failCount + "/" + maxLoginFailCount + ")";
}
session.setAttribute(RESULT_MSG, msg);
}
// 서비스 타입 검증
@@ -19,6 +19,7 @@ import com.eactive.eai.rms.data.entity.onl.bzwkdstcd.UserBusinessService;
import com.eactive.eai.rms.data.entity.onl.layout.LayoutItemEMSRepository;
import com.eactive.eai.rms.data.entity.onl.layout.LayoutService;
import com.eactive.eai.rms.onl.manage.rule.layoutsync.LayoutSyncVo;
import com.eactive.eai.transformer.layout.Item;
@Repository("layoutDao")
@SuppressWarnings("unchecked")
@@ -122,8 +123,25 @@ public class LayoutDao extends SqlMapClientTemplateDao {
entity.setLoutitempathname((String) param.get("loutItemPathName"));
entity.setLoutitemlencnt((Integer) param.get("loutItemLenCnt"));
entity.setLoutitemrefinfo((String) param.get("loutItemRefInfo"));
entity.setLoutitemrefinfo2((String) param.get("loutItemRefInfo2"));
entity.setLoutitemoccurptrndstcd((String) param.get("loutItemOccurPtrnDstcd"));
// djb cumstom : 반복 참조 필드에 .이 포함되서 오면 .이후의 값만 저장
String loutItemRefInfo2 = (String) param.get("loutItemRefInfo2");
if (loutItemRefInfo2 != null && loutItemRefInfo2.indexOf(".") > -1) {
loutItemRefInfo2 = loutItemRefInfo2.substring(loutItemRefInfo2.lastIndexOf(".")+1);
}
entity.setLoutitemrefinfo2(loutItemRefInfo2);
// djb custom : GRIP(=GROUP)이고 반복횟수=1 일때는 반복횟수 넣지않는다. ==> GROUP으로 처리
Integer loutItemNodePtrnIDName = (Integer) param.get("loutItemNodePtrnIDName");
String loutItemOccurPtrnDstcd = (String) param.get("loutItemOccurPtrnDstcd");
if (loutItemNodePtrnIDName != null) {
if (loutItemNodePtrnIDName == Item.NODE_GROUP && "1".equals(loutItemOccurPtrnDstcd)) {
loutItemOccurPtrnDstcd = "";
}
}
entity.setLoutitemoccurptrndstcd(loutItemOccurPtrnDstcd);
entity.setLoutitemmaxoccurnoitm((Integer) param.get("loutItemMaxOccurNoitm"));
entity.setLoutitemminoccurnoitm((Integer) param.get("loutItemMinOccurNoitm"));
entity.setLoutitembascval((String) param.get("loutItemBascVal"));
@@ -934,9 +934,9 @@ public class LayoutSyncController extends OnlBaseAnnotationController implements
service.deleteLayout(vo);
CommonCommand commonCommand = new CommonCommand("com.eactive.eai.agent.transformer.RemoveLayoutCommand",vo.get("loutName"));
HashMap<String,String> result = AgentParserUtils.parse(commonCommand, agentUtilService,"prcssRslt","prcssRsltCmnt");
param.putAll(result);
// CommonCommand commonCommand = new CommonCommand("com.eactive.eai.agent.transformer.RemoveLayoutCommand",vo.get("loutName"));
// HashMap<String,String> result = AgentParserUtils.parse(commonCommand, agentUtilService,"prcssRslt","prcssRsltCmnt");
// param.putAll(result);
service.addLayoutSyncLog(param);
} else {
@@ -1024,7 +1024,7 @@ public class LayoutSyncController extends OnlBaseAnnotationController implements
service.insertLayout(vo, list); //FIXME : 스키마 변경 룰 적용해야 함
//Djb custom 서버연동 제외 2026.07.22 jwh
//Djb custom 서버연동 제외
// CommonCommand commonCommand = new CommonCommand("com.eactive.eai.agent.transformer.ReloadLayoutCommand",vo.get("loutName"));
// HashMap<String,String> result = AgentParserUtils.parse(commonCommand, agentUtilService,"prcssRslt","prcssRsltCmnt");
// param.putAll(result);
@@ -1200,6 +1200,8 @@ login.failcountover2 = You have had three password failures. <B
login.grantfail1 = Failed to initialize user credential information.
login.grantfail2 = Logon Error:<BR>Failed to initialize user credential information.
login.loginfail1 = Login failed. Please check your login information.
login.accountlocked1 = Account locked due to {0} or more login failures. Please contact your administrator.
login.accountlocked2 = Account locked due to {0} or more login failures.<BR/> Please contact your administrator.
login.loginfail2 = Login failed.<BR/>Please check your login information.
login.logonaccount = Logon account
login.logout = Logout
@@ -1346,6 +1346,8 @@ login.failcountover2 = You have had three password failures. <B
login.grantfail1 = Failed to initialize user credential information.
login.grantfail2 = Logon Error:<BR>Failed to initialize user credential information.
login.loginfail1 = Login failed. Please check your login information.
login.accountlocked1 = Account locked due to {0} or more login failures. Please contact your administrator.
login.accountlocked2 = Account locked due to {0} or more login failures.<BR/> Please contact your administrator.
login.loginfail2 = Login failed.<BR/>Please check your login information.
login.logonaccount = Logon account
login.logout = Logout
@@ -1426,6 +1426,8 @@ login.loginfail1 = \uC0AC\uC6A9\uC790ID \uB610\uB294 \uBE44
login.loginfail2 = \uC0AC\uC6A9\uC790ID \uB610\uB294 \uBE44\uBC00\uBC88\uD638 \uC785\uB825 \uC2E4\uD328\uC785\uB2C8\uB2E4.<BR/> \uB85C\uADF8\uC778\uC815\uBCF4\uB97C \uD655\uC778\uD558\uC138\uC694.
login.checkstatus1 = \uC7A5\uAE30(90\uC77C)\uBBF8\uC0AC\uC6A9 \uACC4\uC815\uC785\uB2C8\uB2E4. \uB2F4\uB2F9\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
login.checkstatus2 = \uC7A5\uAE30(90\uC77C)\uBBF8\uC0AC\uC6A9 \uACC4\uC815\uC785\uB2C8\uB2E4.<BR/> \uB2F4\uB2F9\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
login.accountlocked1 = \uB85C\uADF8\uC778 {0}\uD68C \uC774\uC0C1 \uC2E4\uD328\uB85C \uACC4\uC815\uC774 \uC7A0\uACBC\uC2B5\uB2C8\uB2E4. \uAD00\uB9AC\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
login.accountlocked2 = \uB85C\uADF8\uC778 {0}\uD68C \uC774\uC0C1 \uC2E4\uD328\uB85C \uACC4\uC815\uC774 \uC7A0\uACBC\uC2B5\uB2C8\uB2E4.<BR/> \uAD00\uB9AC\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
login.logonaccount = \uB85C\uADF8\uC628\uACC4\uC815
login.logout = \uB85C\uADF8\uC544\uC6C3
login.password = \uBE44\uBC00\uBC88\uD638