-
te***@example.com
+
test@example.com
(2024.01.01 가입)
diff --git a/src/main/resources/templates/views/apps/login/login.html b/src/main/resources/templates/views/apps/login/login.html
index d082d54..0c2c7f7 100644
--- a/src/main/resources/templates/views/apps/login/login.html
+++ b/src/main/resources/templates/views/apps/login/login.html
@@ -149,34 +149,21 @@
});
}
- // 로그인 전 중복 접속 확인 → 중복 시 기존 세션 강제 로그아웃 여부 질의
- 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 + ')에서 접속 중입니다.
'
- + '접속 시각: ' + data.loginTime + '
'
- + '기존 접속을 해제하고 로그인하시겠습니까?';
- customPopups.showConfirm(msg, function (confirmed) {
- if (confirmed) {
- $('#loginLoading').addClass('active');
- form.submit();
- }
- });
- } else {
- form.submit();
- }
- },
- error: function () {
- // 중복 체크 실패 시 로그인은 그대로 진행
- form.submit();
- }
- });
+ // 동시 접속 확인은 비밀번호 검증(1차 인증) 통과 후 서버가 안내한다
+ // (2FA pending 또는 /login?duplicate=1 대기 상태에서 duplicateInfo 모델로 수신).
+ function duplicateConfirmMessage(info) {
+ return '해당 계정은 이미 다른 곳(' + info.ipAddress + ')에서 접속 중입니다.
'
+ + '접속 시각: ' + info.loginTime + '
'
+ + '기존 접속을 해제하고 로그인하시겠습니까?';
+ }
+
+ // 세션 CSRF 헤더 (meta[name=_csrf]) — 인증 후 확인/취소 POST 용
+ function csrfHeaders() {
+ var t = document.querySelector('meta[name="_csrf"]');
+ var h = document.querySelector('meta[name="_csrf_header"]');
+ var headers = {};
+ headers[h ? h.getAttribute('content') : 'X-XSRF-TOKEN'] = t ? t.getAttribute('content') : '';
+ return headers;
}
function fnInit() {
@@ -242,7 +229,7 @@
customPopups.showAlert('[[#{login.passLengthShort}]]');
} else {
refreshCsrfAndThen(form, function () {
- checkDuplicateAndLogin(form);
+ form.submit();
});
}
}
@@ -260,9 +247,12 @@
}
});
- // 로그인 2FA: 1차 인증 통과 후 pending 상태면 추가 인증 팝업 자동 오픈
+ // 1차 인증(비밀번호) 통과 후 흐름: [동시 접속 확인] → [2FA] 순서.
var twoFactorPending = [[${twoFactorPending}]];
- if (twoFactorPending && typeof TwoFactorAuth !== 'undefined') {
+ var duplicateConfirmPending = [[${duplicateConfirmPending}]];
+ var duplicateInfo = [[${duplicateInfo}]];
+
+ function openLoginTwoFactor() {
TwoFactorAuth.open({
mode: 'login',
onSuccess: function (res) {
@@ -279,6 +269,53 @@
});
}
+ if (twoFactorPending && typeof TwoFactorAuth !== 'undefined') {
+ // 로그인 2FA pending: 동시 접속이 있으면 확인 후 2FA 팝업, 취소 시 로그인 포기
+ if (duplicateInfo) {
+ customPopups.showConfirm(duplicateConfirmMessage(duplicateInfo), function (confirmed) {
+ if (confirmed) {
+ openLoginTwoFactor();
+ } else {
+ $.ajax({
+ url: /*[[@{/auth/2fa/cancel}]]*/ '/auth/2fa/cancel',
+ type: 'POST',
+ headers: csrfHeaders(),
+ data: { reason: 'CANCELLED' }
+ });
+ customPopups.showAlert('로그인이 취소되었습니다.');
+ }
+ });
+ } else {
+ openLoginTwoFactor();
+ }
+ } else if (duplicateConfirmPending && duplicateInfo) {
+ // 2FA off + 동시 접속: 확인 시 서버가 로그인 확정(기존 접속 해제), 취소 시 포기
+ customPopups.showConfirm(duplicateConfirmMessage(duplicateInfo), function (confirmed) {
+ var url = confirmed
+ ? /*[[@{/login/duplicate/confirm}]]*/ '/login/duplicate/confirm'
+ : /*[[@{/login/duplicate/cancel}]]*/ '/login/duplicate/cancel';
+ $.ajax({
+ url: url,
+ type: 'POST',
+ headers: csrfHeaders(),
+ success: function (res) {
+ if (confirmed) {
+ if (res && res.valid && res.redirect) {
+ window.location.href = res.redirect;
+ } else {
+ customPopups.showAlert((res && res.message) || '로그인 확인이 만료되었습니다. 다시 로그인해주세요.');
+ }
+ }
+ },
+ error: function () {
+ if (confirmed) {
+ customPopups.showAlert('로그인 처리 중 오류가 발생했습니다. 다시 로그인해주세요.');
+ }
+ }
+ });
+ });
+ }
+
fnInit();
});
diff --git a/src/main/resources/templates/views/apps/mypage/passwordChange.html b/src/main/resources/templates/views/apps/mypage/passwordChange.html
index 1446a1f..31e330c 100644
--- a/src/main/resources/templates/views/apps/mypage/passwordChange.html
+++ b/src/main/resources/templates/views/apps/mypage/passwordChange.html
@@ -29,7 +29,8 @@
-
소중한 계정 보호를 위해 비밀번호를 변경해 주세요!
+
소중한 계정 보호를 위해 비밀번호를 변경해 주세요!
+ 비밀번호 변경이 완료되면 자동으로 로그아웃되며, 새 비밀번호로 다시 로그인해야 합니다.
diff --git a/src/main/resources/templates/views/fragment/popup/withdrawalPopup.html b/src/main/resources/templates/views/fragment/popup/withdrawalPopup.html
index 59c58a4..9d40463 100644
--- a/src/main/resources/templates/views/fragment/popup/withdrawalPopup.html
+++ b/src/main/resources/templates/views/fragment/popup/withdrawalPopup.html
@@ -1,5 +1,6 @@
-