Implement SMS two-factor authentication with AJAX support
This commit is contained in:
@@ -43,13 +43,6 @@
|
||||
}
|
||||
%>
|
||||
|
||||
<script src="js/jquery-1.12.1.min.js"></script>
|
||||
<!-- <script src="js/popper.js"></script> -->
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="css/login-style.css">
|
||||
<link rel="stylesheet" href="css/login-fontawesome_all.css">
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
|
||||
@@ -59,13 +52,20 @@
|
||||
href="<c:url value="/img/ic_eai_favi_${themeColor}.png"/>"
|
||||
type="image/ico" sizes="16x16" />
|
||||
<title><%=systemModeTitle%><%=localeMessage.getString("screen.title")%></title>
|
||||
<%-- <jsp:include page="/jsp/common/include/css.jsp"/> --%>
|
||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||
<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;
|
||||
|
||||
function fncPreMain() {
|
||||
if ($("input[name=userId]").val() ==null || $("input[name=userId]").val().length == 0 ){
|
||||
alert("<%= localeMessage.getString("login.checkid") %>");
|
||||
@@ -83,7 +83,140 @@
|
||||
return;
|
||||
}
|
||||
sessionStorage["serviceType"]= $("select[name=serviceType]").val();
|
||||
$("#loginForm").submit();
|
||||
|
||||
// AJAX 로그인 요청
|
||||
$.ajax({
|
||||
url: '<c:url value="/login-ajax.do"/>',
|
||||
type: 'POST',
|
||||
data: $('#loginForm').serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.smsAuthRequired) {
|
||||
// SMS 인증 필요
|
||||
showSmsAuthModal(response);
|
||||
} else if (response.success) {
|
||||
// 직접 로그인 성공
|
||||
window.location.href = response.redirectUrl;
|
||||
} else {
|
||||
// 로그인 실패
|
||||
alert(response.errorMessage || '로그인에 실패했습니다.');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Login error:', error);
|
||||
alert('로그인 처리 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// SMS 인증 모달 표시
|
||||
function showSmsAuthModal(data) {
|
||||
$('#smsAuthMaskedPhone').text(data.maskedPhone);
|
||||
$('#smsAuthCode').val('');
|
||||
startExpiryTimer(data.expiresAt);
|
||||
startResendTimer(data.resendableAt);
|
||||
$('#smsAuthModal').modal('show');
|
||||
}
|
||||
|
||||
// 유효시간 타이머
|
||||
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) {
|
||||
startExpiryTimer(data.expiresAt);
|
||||
startResendTimer(data.resendableAt);
|
||||
alert('인증번호가 재발송되었습니다.');
|
||||
} else {
|
||||
alert(data.errorMessage || '재발송에 실패했습니다.');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('재발송 요청 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// SMS 인증번호 확인
|
||||
function verifySmsAuthCode() {
|
||||
var code = $('#smsAuthCode').val();
|
||||
if (!code || code.length !== 6) {
|
||||
alert('6자리 인증번호를 입력해주세요.');
|
||||
$('#smsAuthCode').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '<c:url value="/sms-auth/verify.json"/>',
|
||||
type: 'POST',
|
||||
data: { authCode: code },
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (data.success) {
|
||||
$('#smsAuthModal').modal('hide');
|
||||
window.location.href = data.redirectUrl;
|
||||
} else {
|
||||
alert(data.errorMessage || '인증에 실패했습니다.');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('인증 요청 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// SMS 인증 취소
|
||||
function cancelSmsAuth() {
|
||||
clearInterval(smsAuthTimer);
|
||||
clearInterval(resendTimer);
|
||||
$('#smsAuthModal').modal('hide');
|
||||
}
|
||||
function changePwd(){
|
||||
if ($("input[name=resetUserId]").val() == null || $("input[name=resetUserId]").val().length == 0 ){
|
||||
@@ -212,11 +345,13 @@
|
||||
<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 로그인 버튼 -->
|
||||
<!-- 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>
|
||||
|
||||
@@ -258,6 +393,31 @@
|
||||
</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"
|
||||
onkeypress="return event.charCode >= 48 && event.charCode <= 57">
|
||||
</div>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user