Files
eapim-admin/WebContent/jsp/common/screen/emergency.jsp
T
eastargh da397b21b0
eapim-admin CI / build (push) Has been cancelled
로그인 에러 메세지 표출
2026-09-07 19:04:18 +09:00

544 lines
20 KiB
Plaintext

<%@ page language="java" contentType="text/html; charset=utf-8"%>
<%@page import="com.eactive.eai.rms.common.datasource.DataSourceType"%>
<%@page import="com.eactive.eai.rms.common.datasource.DataSourceTypeManager"%>
<%@page import="com.eactive.eai.common.util.SystemUtil"%>
<%@page import="com.eactive.eai.rms.env.EmsConfig"%>
<%@ 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" />
<%
String sysMode = (String)System.getProperty("eai.systemmode");
String sysModeName = "";
/* System.out.println("##### SERVER_TYPE ====>"+sysMode); */
if( sysMode == null ) {
sysMode = "";
}
if (sysMode.equals("T")) {
sysModeName = "테스트";
} else if (sysMode.equals("D")){
sysModeName = "개발";
}else {
sysModeName = "";
}
String systemModeTitle = SystemUtil.getSystemModeTitle();
String drmode = System.getProperty(EmsConfig.DR_MODE);
if ("Y".equals(drmode)) {
systemModeTitle = "[DR] ";
}
%>
<%
String resultMsg = (String)session.getAttribute("resultMsg");
session.removeAttribute("resultMsg");
session.removeAttribute("SSO_ID");
request.removeAttribute("SSO_ID");
if( resultMsg == null ) {
resultMsg = "";
}
%>
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-control" CONTENT="no-cache">
<link rel="icon"
href="<c:url value="/img/ic_eai_favi_${themeColor}.png"/>"
type="image/ico" sizes="16x16" />
<title><%=systemModeTitle%><%=localeMessage.getString("screen.title")%></title>
<link rel="stylesheet" href="<c:url value="/css/login-style.css"/>">
<link rel="stylesheet" href="<c:url value="/css/login-fontawesome_all.css"/>">
<link rel="stylesheet" href="<c:url value="/addon/bootstrap-4.6.2/css/bootstrap.min.css"/>">
<script src="<c:url value="/js/jquery-1.12.1.min.js"/>"></script>
<script src="<c:url value="/addon/bootstrap-4.6.2/js/bootstrap.bundle.min.js"/>"></script>
<script type="text/javascript">
if( self.name == 'mainFrame' || self.name == 'leftFrame' || self.name == 'topFrame') {
parent.window.location.href='<c:url value="/"/>';
}
// SMS 인증 관련 변수
var smsAuthTimer = null;
var resendTimer = null;
var smsAuthPurpose = 'LOGIN'; // 'LOGIN' | 'PASSWORD_CHANGE' - SMS 인증 모달을 어느 흐름에서 띄웠는지 구분
function fncPreMain() {
if ($("input[name=userId]").val() ==null || $("input[name=userId]").val().length == 0 ){
alert("<%= localeMessage.getString("login.checkid") %>");
$("input[name=userId]").trigger("focus");
return;
}
if ($("input[name=password]").val() ==null || $("input[name=password]").val().length == 0 ){
alert("<%= localeMessage.getString("login.checkpwd1") %>");
$("input[name=password]").trigger("focus");
return;
}
if ($("select[name=serviceType]").val() ==null|| $("select[name=serviceType]").val().length == 0 ){
alert("<%= localeMessage.getString("login.checkservicetype") %>");
$("select[name=serviceType]").trigger("focus");
return;
}
sessionStorage["serviceType"]= $("select[name=serviceType]").val();
// AJAX 로그인 요청
$.ajax({
url: '<c:url value="/login-ajax.do"/>',
type: 'POST',
data: $('#loginForm').serialize(),
dataType: 'json',
success: function(response) {
console.log('res', response)
if (response.changePassword) {
showChangeInitPassword(response);
}
else if (response.smsAuthRequired) {
// SMS 인증 필요
smsAuthPurpose = 'LOGIN';
showSmsAuthModal(response);
} else if (response.success) {
// 직접 로그인 성공
window.location.href = response.redirectUrl;
} else {
if (response.errorMessage == 'NO_PHONE_NUMBER') {
alert("휴대폰번호가 등록되어 있지 않아 로그인할 수 없습니다. 관리자에게 문의하세요")
} else if (response.errorMessage == 'FAIL_SEND_AUTH') {
alert("인증번호 발송에 실패했습니다. 잠시 후 다시 시도해주세요.")
} else {
// 로그인 실패
alert('로그인에 실패했습니다');
}
}
},
error: function(xhr, status, error) {
console.error('Login error:', error);
alert('로그인 처리 중 오류가 발생했습니다.');
}
});
}
function showChangeInitPassword(data) {
$('input[name="resetUserId"]').val($('input[name="userId"]').val());
$('input[name="resetPassword"]').val($('input[name="password"]').val());
$('#changeInitPassword').hide();
$('#pwdChgModal').modal('show');
}
// SMS 인증 모달 표시
function showSmsAuthModal(data) {
$('#smsAuthMaskedPhone').text(data.maskedPhone);
$('#smsAuthCode').val('');
updateSmsTestHint(data.smsTestHint);
startExpiryTimer(data.expiresAt);
startResendTimer(data.resendableAt);
$('#smsAuthModal').modal('show');
}
// fixed 모드(djb.sms_auth.mode=fixed)일 때만 서버가 내려주는 테스트용 인증번호 힌트 표시
function updateSmsTestHint(hint) {
if (hint) {
$('#smsAuthTestHint').text(hint).show();
} else {
$('#smsAuthTestHint').text('').hide();
}
}
// 유효시간 타이머
function startExpiryTimer(expiresAt) {
clearInterval(smsAuthTimer);
updateRemainingTime(expiresAt);
smsAuthTimer = setInterval(function() {
var remaining = Math.max(0, Math.floor((expiresAt - Date.now()) / 1000));
$('#smsAuthRemainingTime').text(remaining);
if (remaining <= 0) {
clearInterval(smsAuthTimer);
alert('인증 시간이 만료되었습니다. 다시 시도해주세요.');
$('#smsAuthModal').modal('hide');
}
}, 1000);
}
function updateRemainingTime(expiresAt) {
var remaining = Math.max(0, Math.floor((expiresAt - Date.now()) / 1000));
$('#smsAuthRemainingTime').text(remaining);
}
// 재발송 타이머
function startResendTimer(resendableAt) {
$('#btnSmsResend').prop('disabled', true);
clearInterval(resendTimer);
updateResendButtonText(resendableAt);
resendTimer = setInterval(function() {
var remaining = Math.max(0, Math.ceil((resendableAt - Date.now()) / 1000));
if (remaining <= 0) {
$('#btnSmsResend').prop('disabled', false).text('재발송');
clearInterval(resendTimer);
} else {
$('#btnSmsResend').text('재발송 (' + remaining + '초)');
}
}, 1000);
}
function updateResendButtonText(resendableAt) {
var remaining = Math.max(0, Math.ceil((resendableAt - Date.now()) / 1000));
if (remaining > 0) {
$('#btnSmsResend').text('재발송 (' + remaining + '초)');
} else {
$('#btnSmsResend').text('재발송');
}
}
// SMS 인증번호 재발송
function resendSmsAuthCode() {
$.ajax({
url: '<c:url value="/sms-auth/resend.json"/>',
type: 'POST',
dataType: 'json',
success: function(data) {
if (data.sent) {
updateSmsTestHint(data.smsTestHint);
startExpiryTimer(data.expiresAt);
startResendTimer(data.resendableAt);
alert('인증번호가 재발송되었습니다.');
} else {
alert(data.errorMessage || '재발송에 실패했습니다.');
}
},
error: function() {
alert('재발송 요청 중 오류가 발생했습니다.');
}
});
}
// SMS 인증번호 확인 (로그인 / 비밀번호 변경 공용 - smsAuthPurpose로 분기)
function verifySmsAuthCode() {
var code = $('#smsAuthCode').val();
if (!code || code.length !== 6) {
alert('6자리 인증번호를 입력해주세요.');
$('#smsAuthCode').focus();
return;
}
var isPasswordChange = (smsAuthPurpose === 'PASSWORD_CHANGE');
var verifyUrl = isPasswordChange
? '<c:url value="/sms-auth/verify-password-change.json"/>'
: '<c:url value="/sms-auth/verify.json"/>';
$.ajax({
url: verifyUrl,
type: 'POST',
data: { authCode: code },
dataType: 'json',
success: function(data) {
if (data.success) {
$('#smsAuthModal').modal('hide');
if (isPasswordChange) {
alert(data.message);
}
window.location.href = data.redirectUrl;
} else {
alert(data.errorMessage || data.message || '인증에 실패했습니다.');
}
},
error: function() {
alert('인증 요청 중 오류가 발생했습니다.');
}
});
}
// SMS 인증 취소
function cancelSmsAuth() {
clearInterval(smsAuthTimer);
clearInterval(resendTimer);
$('#smsAuthModal').modal('hide');
}
function checkPwd(){
if ($("input[name=resetUserId]").val() == null || $("input[name=resetUserId]").val().length == 0 ){
alert("<%= localeMessage.getString("login.checkid") %>");
$("input[name=resetUserId]").trigger("focus");
return false;
}
if ($("input[name=resetPassword]").val() == null || $("input[name=resetPassword]").val().trim().length == 0 ){
alert("<%= localeMessage.getString("login.checkpwd1") %>");
$("input[name=resetPassword]").trigger("focus");
return false;
}
if ($("input[name=changePassword]").val() == null || $("input[name=changePassword]").val().trim().length == 0 ){
alert("<%= localeMessage.getString("login.checkpwd2") %>");
$("input[name=changePassword]").trigger("focus");
return false;
}
if ($("input[name=confirmPassword]").val() == null || $("input[name=confirmPassword]").val().trim().length == 0 ){
alert("<%= localeMessage.getString("login.checkpwd3") %>");
return false;
}
if ($("input[name=changePassword]").val() != $("input[name=confirmPassword]").val()){
alert("<%= localeMessage.getString("login.checkpwd4") %>");
$("input[name=confirmPassword]").trigger("focus");
return false;
}
if ($("input[name=resetUserId]").val() == $("input[name=changePassword]").val()){
alert("<%= localeMessage.getString("login.checkpwd5") %>");
$("input[name=changePassword]").trigger("focus");
return false;
}
return true;
}
// 비밀번호 변경 요청 (AJAX) - 서버 룰 검증 실패 시 모달을 닫지 않고 비밀번호 입력값만 초기화
function submitChangePassword() {
if (!checkPwd()) {
return false;
}
$.ajax({
url: '<c:url value="/changePassword.do"/>',
type: 'POST',
data: $('#modalLoginForm').serialize(),
dataType: 'json',
success: function(response) {
if (response.smsAuthRequired) {
// 비밀번호는 아직 반영되지 않음 - SMS 인증 완료 시 최종 반영됨
smsAuthPurpose = 'PASSWORD_CHANGE';
// 두 모달이 겹치면 이전 모달의 enforceFocus/backdrop 이 남아 입력이 막히므로
// #pwdChgModal 이 완전히 닫힌 뒤 SMS 인증 모달을 연다
$('#pwdChgModal').one('hidden.bs.modal', function() {
showSmsAuthModal(response);
});
$('#pwdChgModal').modal('hide');
return;
}
alert(response.message);
if (response.success) {
window.location.href = response.redirectUrl || '<c:url value="/emergency.jsp"/>';
} else {
$("input[name=changePassword]").val('');
$("input[name=confirmPassword]").val('');
$("input[name=changePassword]").trigger("focus");
}
},
error: function() {
alert("<%= localeMessage.getString("login.pwdchangefail") %>");
}
});
return false;
}
$(document).ready(function() {
$("input[name=userId]").keydown(function(event){
if ( event.which == 13 ) {
fncPreMain();
}
});
$("input[name=password]").keydown(function(event){
if ( event.which == 13 ) {
fncPreMain();
}
});
$("input[name=changePassword]").keydown(function(event){
if ( event.which == 13 ) {
event.preventDefault();
submitChangePassword();
}
});
$("input[name=confirmPassword]").keydown(function(event){
if ( event.which == 13 ) {
event.preventDefault();
submitChangePassword();
}
});
// Password Change 링크로 직접 열 때만 초기화 (JS에서 modal('show') 호출 시 relatedTarget 없음)
$('#pwdChgModal').on('show.bs.modal', function(event) {
if (event.relatedTarget) {
$('#modalLoginForm')[0].reset();
$('#changeInitPassword').show();
}
});
$("select[name=serviceType]").change(function(){
fncPreMain();
});
$("#btn_login").click(function(){
fncPreMain();
});
$("#btn_sso_login").click(function(){
fncSsoLogin();
});
// SMS 인증번호 입력 필드에서 엔터키 누르면 확인
$("#smsAuthCode").keydown(function(event){
if ( event.which == 13 ) {
verifySmsAuthCode();
}
});
// 인증번호는 숫자만 허용 (타이핑/붙여넣기/드래그 공통, 백스페이스·방향키는 방해하지 않음)
$("#smsAuthCode").on("input", function(){
var digits = this.value.replace(/[^0-9]/g, '').slice(0, 6);
if (this.value !== digits) {
this.value = digits;
}
});
// SMS 인증 모달이 완전히 열린 뒤 인증번호 입력창에 포커스
$('#smsAuthModal').on('shown.bs.modal', function() {
$('#smsAuthCode').trigger('focus');
});
});
function fncSsoLogin() {
sessionStorage["serviceType"] = $("select[name=serviceType]").val();
location.href = '<c:url value="/sso/login.do"/>';
}
</script>
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-left mb-3">
<div class="col-md-12 text-center">
<img style="width: 20%;opacity: 0.4;" src='<c:url value="/img/logo_eactive.png"/>'/>
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<h1 style="vertical-align: middle !important" class="h1"><span class="text-danger">e</span><span class="text-info">Link Monitoring Admin</span></h1>
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-7 col-lg-5">
<div class="login-wrap p-4 p-md-5">
<div class="icon text-danger d-flex align-items-center justify-content-center">
<img style="width: 40px;" src='<c:url value="/img/login/user.png"/>'/>
</div>
<h3 class="text-center mb-4">Log In</h3>
<form name="form" id="loginForm" action="<c:url value="/login.do"/>" method="post">
<div class="form-group">
<input type="text" name="userId" class="form-control rounded-left" placeholder="Id" required>
</div>
<div class="form-group d-flex">
<input type="password" name="password" class="form-control rounded-left" placeholder="Password" autocomplete="off" required>
</div>
<div class="form-group">
<select name="serviceType" class="form-control rounded-left" placeholder="system" required>
<%
for (DataSourceType dt : DataSourceTypeManager.getDynamicDataSourceTypes()){
%>
<option value="<%=dt.getName()%>"><%=dt.getText()%></option>
<%
}
%>
</select>
</div>
<div class="form-group">
<input type="button" id ="btn_login" class="form-control btn btn-primary rounded submit px-3" value="Login">
</div>
<div style="text-align: right !important;" class="form-group">
<a style="font-size: 12px !important;" href="#pwdChgModal" data-toggle="modal" >Password Change</a>
</div>
<!-- SSO 로그인 버튼 (비상모드 시 숨김) -->
<c:if test="${not emergencyMode}">
<!-- <div class="form-group">
<input type="button" id="btn_sso_login" class="form-control btn btn-success rounded submit px-3"
value="SSO Login">
</div> -->
</c:if>
<p style="color:red;"><%=resultMsg %></p>
</form>
</div>
</div>
</div>
</div>
<!-- 비밀번호 변경 모달 -->
<div class="modal fade" id="pwdChgModal" tabindex="-1" role="dialog" aria-labelledby="pwdChgModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="pwdChgModalLabel">Password 설정</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">X</span>
</button>
</div>
<form id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post" onsubmit="return submitChangePassword()" novalidate>
<div class="modal-body">
<div id="changeInitPassword">
<div class="form-group d-flex">
<input type="text" name="resetUserId" class="form-control rounded-left" placeholder="User Id" required>
</div>
<div class="form-group d-flex">
<input type="password" name="resetPassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderCurrentPassword") %>" autocomplete="off" required>
</div>
</div>
<p><%= localeMessage.getString("login.toChangePasswordMessage") %></p>
<div class="form-group d-flex">
<input type="password" name="changePassword" class="form-control rounded-left" placeholder="<%= localeMessage.getString("login.placeholderChangePassword") %>" autocomplete="off" required>
</div>
<div class="form-group d-flex">
<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">
※ <%= pwdLengthCheck %>글자 이상 & 영문/숫자/특수문자 <%= pwdCombiCheck %>종류 이상<br/>
※ 연속된 숫자/문자(예: 123, abc, qwer) 사용 불가<%= pwdRepeatCheck >= 2 ? ", 동일 문자 " + pwdRepeatCheck + "자 이상 반복 불가" : "" %>
</span>
</div>
<div class="modal-footer">
<button type="submit" id="changePwd" class="form-control btn btn-primary rounded submit px-3">변경</button>
</div>
</form>
</div>
</div>
</div>
<!-- SMS 인증 모달 -->
<div class="modal fade" id="smsAuthModal" tabindex="-1" role="dialog" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">SMS 인증</h5>
</div>
<div class="modal-body">
<p>인증번호가 <strong><span id="smsAuthMaskedPhone"></span></strong>로 발송되었습니다.</p>
<div class="form-group">
<input type="text" id="smsAuthCode" class="form-control"
placeholder="인증번호 6자리" maxlength="6" inputmode="numeric" autocomplete="off">
</div>
<p id="smsAuthTestHint" class="text-warning" style="display:none;"></p>
<p class="text-muted">남은 시간: <span id="smsAuthRemainingTime" class="text-danger font-weight-bold">60</span>초</p>
</div>
<div class="modal-footer">
<button type="button" id="btnSmsResend" class="btn btn-secondary" onclick="resendSmsAuthCode()" disabled>재발송</button>
<button type="button" class="btn btn-primary" onclick="verifySmsAuthCode()">확인</button>
<button type="button" class="btn btn-danger" onclick="cancelSmsAuth()">취소</button>
</div>
</div>
</div>
</div>
</body>
</html>