- 로그인 중복 세션 확인 및 기존 세션 강제 로그아웃 처리 추가

- 세션 타이머 데스크톱·모바일 표시 및 경고 스타일 동기화
This commit is contained in:
Rinjae
2026-06-19 18:06:40 +09:00
parent 7265ad6589
commit fc96c13df2
7 changed files with 68 additions and 11 deletions
+8
View File
@@ -5333,6 +5333,14 @@ select.form-control {
font-weight: 700; font-weight: 700;
} }
.session-timer-mobile {
margin-right: 8px;
font-size: 12px;
}
.session-timer-mobile .session-timer-text {
min-width: 34px;
}
.data-table { .data-table {
width: 100%; width: 100%;
background: #FFFFFF; background: #FFFFFF;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -31,3 +31,13 @@
} }
} }
} }
// 모바일 헤더(햄버거 버튼 좌측)용 타이머
.session-timer-mobile {
margin-right: $spacing-sm;
font-size: $font-size-xs;
.session-timer-text {
min-width: 34px;
}
}
@@ -115,6 +115,36 @@
form.checkId.checked = ((form.id.value = getCookie('saveid')) !== null); form.checkId.checked = ((form.id.value = getCookie('saveid')) !== null);
} }
// 로그인 전 중복 접속 확인 → 중복 시 기존 세션 강제 로그아웃 여부 질의
function checkDuplicateAndLogin(form) {
var loginId = form.id.value;
$.ajax({
url: /*[[@{/api/session/check-duplicate}]]*/ '/api/session/check-duplicate',
type: 'POST',
data: { loginId: loginId },
success: function (data) {
if (data && data.duplicateSession) {
$('#loginLoading').removeClass('active');
var msg = '해당 계정은 이미 다른 곳(' + data.ipAddress + ')에서 접속 중입니다.<br>'
+ '접속 시각: ' + data.loginTime + '<br><br>'
+ '기존 접속을 해제하고 로그인하시겠습니까?';
customPopups.showConfirm(msg, function (confirmed) {
if (confirmed) {
$('#loginLoading').addClass('active');
form.submit();
}
});
} else {
form.submit();
}
},
error: function () {
// 중복 체크 실패 시 로그인은 그대로 진행
form.submit();
}
});
}
function fnInit() { function fnInit() {
let form = document.getElementById('loginForm'); let form = document.getElementById('loginForm');
if (form) { if (form) {
@@ -179,7 +209,7 @@
$('#loginLoading').removeClass('active'); $('#loginLoading').removeClass('active');
customPopups.showAlert('[[#{login.passLengthShort}]]'); customPopups.showAlert('[[#{login.passLengthShort}]]');
} else { } else {
form.submit(); checkDuplicateAndLogin(form);
} }
} }
form.classList.add('was-validated'); form.classList.add('was-validated');
@@ -107,6 +107,10 @@
</div> </div>
<div class="mobile-right"> <div class="mobile-right">
<span class="session-timer session-timer-mobile" sec:authorize="isAuthenticated()" title="남은 세션 시간">
<i class="fas fa-clock session-timer-icon"></i>
<span class="session-timer-text">--:--</span>
</span>
<button class="mobile-menu-btn" id="mobileMenuToggle" aria-label="메뉴"> <button class="mobile-menu-btn" id="mobileMenuToggle" aria-label="메뉴">
<span class="hamburger-line"></span> <span class="hamburger-line"></span>
<span class="hamburger-line"></span> <span class="hamburger-line"></span>
@@ -261,7 +265,8 @@
var WARNING_SECONDS = 60; // 만료 60초 전 연장 확인 모달 var WARNING_SECONDS = 60; // 만료 60초 전 연장 확인 모달
var POLL_INTERVAL_MS = 30000; // 서버 잔여시간 동기화 주기 var POLL_INTERVAL_MS = 30000; // 서버 잔여시간 동기화 주기
var timerText = document.getElementById('sessionRemainingTime'); // 데스크톱·모바일 타이머 텍스트를 모두 갱신 (id 대신 클래스 사용)
var timerTexts = document.querySelectorAll('.session-timer-text');
var countdownTimer = null; var countdownTimer = null;
var promptShown = false; // 연장 확인 모달 중복 표시 방지 var promptShown = false; // 연장 확인 모달 중복 표시 방지
var redirecting = false; var redirecting = false;
@@ -284,12 +289,16 @@
} }
function render() { function render() {
if (!timerText) return; if (!timerTexts.length) return;
timerText.textContent = formatTime(remainingSeconds); var label = formatTime(remainingSeconds);
if (remainingSeconds <= WARNING_SECONDS) { var warn = remainingSeconds <= WARNING_SECONDS;
timerText.classList.add('session-timer-warning'); for (var i = 0; i < timerTexts.length; i++) {
} else { timerTexts[i].textContent = label;
timerText.classList.remove('session-timer-warning'); if (warn) {
timerTexts[i].classList.add('session-timer-warning');
} else {
timerTexts[i].classList.remove('session-timer-warning');
}
} }
} }