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
@@ -184,8 +184,9 @@ function getEndTime(){
</td>
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.remoteAddress") %></th>
<td><input type="text" name="searchRemoteAddress" value="${param.searchRemoteAddress}" style="width:100%"></td>
<th style="width:180px;"></th>
<th style="width:180px;"><%= localeMessage.getString("auditLogMan.parameters") %></th>
<td>
<input type="text" name="searchParameters" value="${param.searchParameters}" style="width:100%">
</td>
</tr>
</tbody>
@@ -18,6 +18,16 @@
<script language="javascript" >
var url = '<c:url value="/common/acl/user/userMan.json" />';
var url_view = '<c:url value="/common/acl/user/userMan.view" />';
function userStatusFormat (cellvalue){
if ( cellvalue == '1' ){
return '<%= localeMessage.getString("code.normal") %>';
}else if ( cellvalue == '2' ) {
return '<span style="color:red"><%= localeMessage.getString("code.lock") %></span>';
}
return '';
}
$(document).ready(function() {
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
$('#grid').jqGrid({
@@ -28,6 +38,7 @@ $(document).ready(function() {
colNames:['<%= localeMessage.getString("login.userId") %>',
'<%= localeMessage.getString("user.name") %>',
'<%= localeMessage.getString("user.roleName") %>',
'<%= localeMessage.getString("user.status") %>',
'<%= localeMessage.getString("user.createOn") %>',
'<%= localeMessage.getString("user.depart") %>',
'<%= localeMessage.getString("user.teamName") %>',
@@ -37,6 +48,7 @@ $(document).ready(function() {
{ name : 'USERID' , align:'center' ,sortable:false },
{ name : 'USERNAME' , align:'center' },
{ name : 'ROLEIDNFINAME' , align:'center' },
{ name : 'STATUS' , align:'center', formatter: userStatusFormat },
{ name : 'LASTAMNDYMS' , align:'center', formatter: timeStampFormat },
{ name : 'DVSNNAME' , align:'center', hidden: true },
{ name : 'TEAMNAME' , align:'center', hidden: true },
@@ -216,6 +216,7 @@ $(document).ready(function() {
dataType:"json",
data:{cmd: 'UPDATE_PASSWORD', userId : $("input[name=userId]").val()},
success:function(json){
$('select[name="status"]').val("1")
alert(json.result);
},
error:function(e){
@@ -277,10 +278,10 @@ $(document).ready(function() {
<table class="table_row" cellspacing="0">
<input type="hidden" name="jobclcd">
<tr>
<th style="width:180px;"><%= localeMessage.getString("login.userId") %></th><td ><input type="text" name="userId"/> </td>
<th style="width:180px;"><%= localeMessage.getString("login.userId") %> <span class=\"required-mark\">*</span></th><td ><input type="text" name="userId"/> </td>
</tr>
<tr>
<th><%= localeMessage.getString("user.name") %> </th><td><input type="text" name="userName" /></td>
<th><%= localeMessage.getString("user.name") %> <span class=\"required-mark\">*</span></th><td><input type="text" name="userName" /></td>
</tr>
<tr class="datailT">
<th><%= localeMessage.getString("user.auth") %> </th><td><input type="text" name="roleidnfiname" /></td>
@@ -317,7 +318,6 @@ $(document).ready(function() {
<td>
<div class="select-style">
<select name="status">
<option value=""><%= localeMessage.getString("code.normal") %></option>
<option value="1"><%= localeMessage.getString("code.normal") %></option>
<option value="2"><%= localeMessage.getString("code.lock") %></option>
</select>
@@ -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")
@@ -42,7 +42,7 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class UserManService extends BaseService {
private static final String USER_STATUS_NORMAL = "1";
private final UserInfoService userInfoService;
private final UserRoleService userRoleService;
@@ -100,6 +100,7 @@ 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, "@!");
@@ -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);
}
@@ -266,6 +266,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,9 +348,19 @@ public class MainController implements InterceptorSkipController {
//logger.info("사용자 계정 상태: " + userInfo.getStatus());
if ( userInfo.getStatus() != null && "2".equals(userInfo.getStatus()) ) {
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;
}
@@ -374,8 +387,9 @@ 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 {
@@ -389,8 +403,9 @@ public class MainController implements InterceptorSkipController {
//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;
}
}
@@ -432,6 +447,27 @@ public class MainController implements InterceptorSkipController {
return userInfo;
}
// 최대 로그인 실패 허용횟수 조회 (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");
@@ -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