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

This commit is contained in:
Rinjae
2026-09-03 15:46:50 +09:00
12 changed files with 84 additions and 19 deletions
+15 -2
View File
@@ -7,6 +7,19 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ include file="/jsp/common/include/localemessage.jsp" %>
<%
// 비밀번호 규칙 안내문구용 모니터링 프로퍼티 조회
com.eactive.eai.rms.common.context.MonitoringContext monitoringContext =
(com.eactive.eai.rms.common.context.MonitoringContext) WebApplicationContextUtils
.getRequiredWebApplicationContext(pageContext.getServletContext())
.getBean("monitoringContext");
int pwdLengthCheck = monitoringContext.getIntProperty(
com.eactive.eai.rms.common.context.MonitoringContext.RMS_PASSWORD_LENGTH_CHECK, 7);
int pwdCombiCheck = monitoringContext.getIntProperty(
com.eactive.eai.rms.common.context.MonitoringContext.RMS_PASSWORD_COMBI_CHECK, 2);
int pwdRepeatCheck = monitoringContext.getIntProperty(
com.eactive.eai.rms.common.context.MonitoringContext.RMS_PASSWORD_REPEAT_CHECK, 3);
%>
<c:set var="themeColor" value="<%=System.getProperty(\"theme.color\")%>" scope="session" />
<%
@@ -481,8 +494,8 @@
<input type="password" name="confirmPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderConfirmationPassword") %>" autocomplete="off" required>
</div>
<span style="font-size:0.8em; color:red">
7글자 이상 & 영문/숫자/특수문자 2종류 이상<br/>
※ 연속된 숫자/문자(예: 123, abc, qwer), 동일 문자 3자 이상 반복 사용 불가
<%= pwdLengthCheck %>글자 이상 & 영문/숫자/특수문자 <%= pwdCombiCheck %>종류 이상<br/>
※ 연속된 숫자/문자(예: 123, abc, qwer) 사용 불가<%= pwdRepeatCheck >= 2 ? ", 동일 문자 " + pwdRepeatCheck + "자 이상 반복 불가" : "" %>
</span>
</div>
<div class="modal-footer">
@@ -179,8 +179,8 @@
autowidth: true,
viewrecords : true,
gridview : true,
ondblClickRow : function(rowId) {
console.log("rowId", rowId);
ondblClickRow : function(rowId) {
/* console.log("rowId", rowId);
var rowData = $(this)
.getRowData(rowId);
var key = rowData['EaiSevrInstncName'];
@@ -193,7 +193,7 @@
url2 += "&searchAdptrBzwkGroupName="
+ key2;
url2 += '&menuId='+'${param.menuId}';
goNav(url2);
goNav(url2); */
},
loadComplete : function(d) {
$("button[name*=img]").unbind("click");
@@ -97,7 +97,7 @@
<div class="search_wrap">
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
</div>
<div class="title" id="title">전문레이아웃 연동 이력 상세</div>
<div class="title" id="title">연동 배포 이력 상세</div>
<table class="table_row" cellspacing="0">
<colgroup>
<col style="width:12%"/><col style="width:38%"/>
@@ -530,6 +530,10 @@
if (confirm("<%= localeMessage.getString("common.checkSave")%>") != true) return;
var formData = new FormData($("#ajaxForm")[0]);
// Summernote 그림 다이얼로그의 내부 파일 input(name="files")이 #ajaxForm 안에 있어
// 선택한 이미지가 지워지지 않고 남아 있으면 여기서 함께 딸려 온다.
// 본 화면은 첨부파일 기능을 쓰지 않으므로 항상 제거한다.
formData.delete("files");
formData.set("useYn", $("#useYn").is(":checked") ? "Y" : "N");
formData.set("fixYn", $("#fixYn").is(":checked") ? "Y" : "N");
formData.set("noticeDetail", $('#contents').summernote('code'));
@@ -70,6 +70,7 @@
// 총건수 도넛 차트
var totalDonutOption = {
color: ['#5470C6', '#FAC858', '#EE6666'],
title: {
text: '총 건수 분포',
left: 'center',
@@ -111,7 +112,7 @@
show: true
},
data: [
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
]
@@ -71,6 +71,7 @@
// 총건수 도넛 차트
var totalDonutOption = {
color: ['#5470C6', '#FAC858', '#EE6666'],
title: {
text: '총 건수 분포',
left: 'center',
@@ -112,7 +113,7 @@
show: true
},
data: [
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
]
@@ -63,6 +63,9 @@
// 총건수 도넛 차트
var totalDonutOption = {
// updateCharts()에서 series[].data를 itemStyle 없이 재설정하면 조각별 itemStyle.color가
// 병합 과정에서 유실되므로, 옵션 레벨 color 팔레트(성공/Timeout/시스템오류 순)로 색을 고정한다.
color: ['#5470C6', '#FAC858', '#EE6666'],
title: {
text: '총 건수 분포',
left: 'center',
@@ -104,7 +107,7 @@
show: true
},
data: [
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
]
@@ -58,6 +58,7 @@
// 총건수 도넛 차트
var totalDonutOption = {
color: ['#5470C6', '#FAC858', '#EE6666'],
title: {
text: '총 건수 분포',
left: 'center',
@@ -99,7 +100,7 @@
show: true
},
data: [
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
]
@@ -58,6 +58,7 @@
// 총건수 도넛 차트
var totalDonutOption = {
color: ['#5470C6', '#FAC858', '#EE6666'],
title: {
text: '총 건수 분포',
left: 'center',
@@ -99,7 +100,7 @@
show: true
},
data: [
{ value: 0, name: '성공', itemStyle: { color: '#91CC75' } },
{ value: 0, name: '성공', itemStyle: { color: '#5470C6' } },
{ value: 0, name: 'Timeout', itemStyle: { color: '#FAC858' } },
{ value: 0, name: '시스템오류', itemStyle: { color: '#EE6666' } }
]
@@ -276,6 +276,15 @@ public interface MonitoringContext {
// 비밀번호 변경 시 SMS 2차 인증 사용 여부 (djb.sms_auth.enabled 와 함께 true 여야 동작)
public static final String RMS_PASSWORD_SMS_AUTH_ENABLED = "rms.password.sms_auth.enabled";
// 비밀번호 최소 길이 (기본 7)
public static final String RMS_PASSWORD_LENGTH_CHECK = "rms.password.length.check";
// 비밀번호 문자 조합 종류 수 (영문/숫자/특수문자 중 N종류 이상, 기본 2)
public static final String RMS_PASSWORD_COMBI_CHECK = "rms.password.combi.check";
// 동일 문자 연속 반복 제한 개수 (기본 3, 2 미만이면 미검사)
public static final String RMS_PASSWORD_REPEAT_CHECK = "rms.password.repeat.check";
// API 상태변화 스윙챗 발송여부
public static final String DJB_UMS_MESSENGER_APIMONITOR_ENABLED = "djb.ums.messenger.api-monitor.enabled";
@@ -67,6 +67,9 @@ public class MainController implements InterceptorSkipController {
private static final String USER_STATUS_LOCKED = "2";
private static final int DEFAULT_MAX_LOGIN_FAIL_COUNT = 5;
private static final int DEFAULT_PASSWORD_HISTORY_COUNT = 5;
private static final int DEFAULT_PASSWORD_LENGTH_CHECK = 7;
private static final int DEFAULT_PASSWORD_COMBI_CHECK = 2;
private static final int DEFAULT_PASSWORD_REPEAT_CHECK = 3;
private final LocaleMessage localeMessage;
private final MonitoringContext monitoringContext;
@@ -469,6 +472,24 @@ public class MainController implements InterceptorSkipController {
return monitoringContext.getBooleanProperty(MonitoringContext.RMS_PASSWORD_SMS_AUTH_ENABLED, false);
}
// 비밀번호 최소 길이 조회 (rms.password.length.check)
private int getPasswordLengthCheck() {
return monitoringContext.getIntProperty(
MonitoringContext.RMS_PASSWORD_LENGTH_CHECK, DEFAULT_PASSWORD_LENGTH_CHECK);
}
// 비밀번호 문자 조합 종류 수 조회 (rms.password.combi.check, 영문/숫자/특수문자 중 N종류 이상)
private int getPasswordCombiCheck() {
return monitoringContext.getIntProperty(
MonitoringContext.RMS_PASSWORD_COMBI_CHECK, DEFAULT_PASSWORD_COMBI_CHECK);
}
// 동일 문자 연속 반복 제한 개수 조회 (rms.password.repeat.check, 2 미만이면 미검사)
private int getPasswordRepeatCheck() {
return monitoringContext.getIntProperty(
MonitoringContext.RMS_PASSWORD_REPEAT_CHECK, DEFAULT_PASSWORD_REPEAT_CHECK);
}
// 로그인 실패 횟수 증가, 임계치 도달 시 계정 잠금
private int increaseLoginFailCount(UserInfo userInfo) {
int failCount = (userInfo.getLoginfailcount() == null ? 0 : userInfo.getLoginfailcount()) + 1;
@@ -1018,10 +1039,17 @@ public class MainController implements InterceptorSkipController {
private boolean isPasswordValid(String password) {
return password != null && password.getBytes().length == password
.length() && password.getBytes().length >= 7
&& isCombination(password)
&& !isRepeatPassword(password, PASSWORD_PATTERN_CHECK_LENGTH)
if (password == null || password.getBytes().length != password.length()) {
return false;
}
int lengthCheck = getPasswordLengthCheck();
int combiCheck = getPasswordCombiCheck();
int repeatCheck = getPasswordRepeatCheck();
return password.getBytes().length >= lengthCheck
&& isCombination(password, combiCheck)
&& (repeatCheck < 2 || !isRepeatPassword(password, repeatCheck))
&& !isSerialPassword(password, PASSWORD_PATTERN_CHECK_LENGTH)
&& !isKeyboardSequentialPassword(password, PASSWORD_PATTERN_CHECK_LENGTH);
}
@@ -1088,7 +1116,7 @@ public class MainController implements InterceptorSkipController {
return false;
}
private boolean isCombination(String password) {
private boolean isCombination(String password, int minTypes) {
int combicount = 0;
// 숫자포함여부
String pattern = "[0-9]+";
@@ -1117,8 +1145,8 @@ public class MainController implements InterceptorSkipController {
combicount++;
}
// combicount가 2 이상이면 true, 그렇지 않으면 false 반환
return combicount >= 2;
// combicount가 minTypes 이상이면 true, 그렇지 않으면 false 반환
return combicount >= minTypes;
}
@@ -60,6 +60,9 @@ public class PortalNoticeManService extends BaseService {
private static final String MANUAL_TITLE_KEYWORD = "장애";
private String decodeString(String value) {
if (value == null) {
return null;
}
if (ContainerUtil.get() == ContainerUtil.TOMCAT) {
try {
return new String(value.getBytes("euc-kr"), StandardCharsets.UTF_8);
@@ -433,7 +436,8 @@ public class PortalNoticeManService extends BaseService {
MultipartFile file = portalNoticeUI.getFiles();
if (file != null && !file.isEmpty()) { // 새로운 파일이 업로드된 경우에만 처리
String decodedFileName = decodeString(portalNoticeUI.getFileName());
String fileName = StringUtils.defaultIfBlank(portalNoticeUI.getFileName(), file.getOriginalFilename());
String decodedFileName = decodeString(fileName);
FileTypeContext.setFileType("notice");
FileInfo fileInfo = fileService.createOrUpdateSingleFile(portalNotice.getFileId(), file, decodedFileName, true);