@@ -65,6 +65,7 @@
|
|||||||
// SMS 인증 관련 변수
|
// SMS 인증 관련 변수
|
||||||
var smsAuthTimer = null;
|
var smsAuthTimer = null;
|
||||||
var resendTimer = null;
|
var resendTimer = null;
|
||||||
|
var smsAuthPurpose = 'LOGIN'; // 'LOGIN' | 'PASSWORD_CHANGE' - SMS 인증 모달을 어느 흐름에서 띄웠는지 구분
|
||||||
|
|
||||||
function fncPreMain() {
|
function fncPreMain() {
|
||||||
if ($("input[name=userId]").val() ==null || $("input[name=userId]").val().length == 0 ){
|
if ($("input[name=userId]").val() ==null || $("input[name=userId]").val().length == 0 ){
|
||||||
@@ -96,6 +97,7 @@
|
|||||||
}
|
}
|
||||||
else if (response.smsAuthRequired) {
|
else if (response.smsAuthRequired) {
|
||||||
// SMS 인증 필요
|
// SMS 인증 필요
|
||||||
|
smsAuthPurpose = 'LOGIN';
|
||||||
showSmsAuthModal(response);
|
showSmsAuthModal(response);
|
||||||
} else if (response.success) {
|
} else if (response.success) {
|
||||||
// 직접 로그인 성공
|
// 직접 로그인 성공
|
||||||
@@ -123,11 +125,21 @@
|
|||||||
function showSmsAuthModal(data) {
|
function showSmsAuthModal(data) {
|
||||||
$('#smsAuthMaskedPhone').text(data.maskedPhone);
|
$('#smsAuthMaskedPhone').text(data.maskedPhone);
|
||||||
$('#smsAuthCode').val('');
|
$('#smsAuthCode').val('');
|
||||||
|
updateSmsTestHint(data.smsTestHint);
|
||||||
startExpiryTimer(data.expiresAt);
|
startExpiryTimer(data.expiresAt);
|
||||||
startResendTimer(data.resendableAt);
|
startResendTimer(data.resendableAt);
|
||||||
$('#smsAuthModal').modal('show');
|
$('#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) {
|
function startExpiryTimer(expiresAt) {
|
||||||
clearInterval(smsAuthTimer);
|
clearInterval(smsAuthTimer);
|
||||||
@@ -181,6 +193,7 @@
|
|||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
if (data.sent) {
|
if (data.sent) {
|
||||||
|
updateSmsTestHint(data.smsTestHint);
|
||||||
startExpiryTimer(data.expiresAt);
|
startExpiryTimer(data.expiresAt);
|
||||||
startResendTimer(data.resendableAt);
|
startResendTimer(data.resendableAt);
|
||||||
alert('인증번호가 재발송되었습니다.');
|
alert('인증번호가 재발송되었습니다.');
|
||||||
@@ -194,7 +207,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// SMS 인증번호 확인
|
// SMS 인증번호 확인 (로그인 / 비밀번호 변경 공용 - smsAuthPurpose로 분기)
|
||||||
function verifySmsAuthCode() {
|
function verifySmsAuthCode() {
|
||||||
var code = $('#smsAuthCode').val();
|
var code = $('#smsAuthCode').val();
|
||||||
if (!code || code.length !== 6) {
|
if (!code || code.length !== 6) {
|
||||||
@@ -203,17 +216,25 @@
|
|||||||
return;
|
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({
|
$.ajax({
|
||||||
url: '<c:url value="/sms-auth/verify.json"/>',
|
url: verifyUrl,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: { authCode: code },
|
data: { authCode: code },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
$('#smsAuthModal').modal('hide');
|
$('#smsAuthModal').modal('hide');
|
||||||
|
if (isPasswordChange) {
|
||||||
|
alert(data.message);
|
||||||
|
}
|
||||||
window.location.href = data.redirectUrl;
|
window.location.href = data.redirectUrl;
|
||||||
} else {
|
} else {
|
||||||
alert(data.errorMessage || '인증에 실패했습니다.');
|
alert(data.errorMessage || data.message || '인증에 실패했습니다.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function() {
|
||||||
@@ -275,6 +296,13 @@
|
|||||||
data: $('#modalLoginForm').serialize(),
|
data: $('#modalLoginForm').serialize(),
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
if (response.smsAuthRequired) {
|
||||||
|
// 비밀번호는 아직 반영되지 않음 - SMS 인증 완료 시 최종 반영됨
|
||||||
|
smsAuthPurpose = 'PASSWORD_CHANGE';
|
||||||
|
$('#pwdChgModal').modal('hide');
|
||||||
|
showSmsAuthModal(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
alert(response.message);
|
alert(response.message);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
window.location.href = response.redirectUrl || '<c:url value="/emergency.jsp"/>';
|
window.location.href = response.redirectUrl || '<c:url value="/emergency.jsp"/>';
|
||||||
@@ -462,6 +490,7 @@
|
|||||||
placeholder="인증번호 6자리" maxlength="6"
|
placeholder="인증번호 6자리" maxlength="6"
|
||||||
onkeypress="return event.charCode >= 48 && event.charCode <= 57">
|
onkeypress="return event.charCode >= 48 && event.charCode <= 57">
|
||||||
</div>
|
</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>
|
<p class="text-muted">남은 시간: <span id="smsAuthRemainingTime" class="text-danger font-weight-bold">60</span>초</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|||||||
@@ -25,4 +25,29 @@ public class ChangePasswordResponseDto {
|
|||||||
* 성공 시 이동할 URL
|
* 성공 시 이동할 URL
|
||||||
*/
|
*/
|
||||||
private String redirectUrl;
|
private String redirectUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMS 2차 인증 필요 여부 (true면 아직 비밀번호가 반영되지 않고 인증 대기 중)
|
||||||
|
*/
|
||||||
|
private boolean smsAuthRequired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 마스킹된 전화번호 (예: 010****1234)
|
||||||
|
*/
|
||||||
|
private String maskedPhone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인증번호 만료 시각 (timestamp, 밀리초)
|
||||||
|
*/
|
||||||
|
private long expiresAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 재발급 가능 시각 (timestamp, 밀리초)
|
||||||
|
*/
|
||||||
|
private long resendableAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* djb.sms_auth.mode=fixed 일 때만 채워지는 테스트용 인증번호 안내 문구 (예: "[테스트] 인증번호: 000000")
|
||||||
|
*/
|
||||||
|
private String smsTestHint;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,4 +50,9 @@ public class LoginResponseDto {
|
|||||||
* 초기 비밀번호 변경 필요 여부
|
* 초기 비밀번호 변경 필요 여부
|
||||||
*/
|
*/
|
||||||
private boolean changePassword;
|
private boolean changePassword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* djb.sms_auth.mode=fixed 일 때만 채워지는 테스트용 인증번호 안내 문구 (예: "[테스트] 인증번호: 000000")
|
||||||
|
*/
|
||||||
|
private String smsTestHint;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ public class MainController implements InterceptorSkipController {
|
|||||||
.maskedPhone(smsAuthService.maskPhoneNumber(userInfo.getCphnno()))
|
.maskedPhone(smsAuthService.maskPhoneNumber(userInfo.getCphnno()))
|
||||||
.expiresAt(smsAuthService.getExpiresAt(session))
|
.expiresAt(smsAuthService.getExpiresAt(session))
|
||||||
.resendableAt(smsAuthService.getResendableAt(session))
|
.resendableAt(smsAuthService.getResendableAt(session))
|
||||||
|
.smsTestHint(smsAuthService.getFixedModeHintText())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,21 +908,41 @@ public class MainController implements InterceptorSkipController {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 비밀번호 변경
|
// 5. SMS 2차 인증 필요 여부 체크 (비상모드는 로그인과 동일하게 우회)
|
||||||
userInfo.setPassword(newPasswordHash);
|
Boolean emergencyMode = (Boolean) session.getAttribute("emergencyMode");
|
||||||
userInfoService.save(userInfo);
|
if (!Boolean.TRUE.equals(emergencyMode) && smsAuthService.isEnabled()) {
|
||||||
|
if (!smsAuthService.hasValidPhoneNumber(userInfo)) {
|
||||||
|
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F", "휴대폰번호 미등록으로 비밀번호 변경 불가");
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(false)
|
||||||
|
.message(toAlertMessage("휴대폰번호가 등록되어 있지 않아 비밀번호를 변경할 수 없습니다. 관리자에게 문의하세요."))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
// 6. 비밀번호 변경 이력 저장 (재사용 금지 검증용)
|
String authCode = smsAuthService.generateAuthCode();
|
||||||
userPasswordHistoryService.recordPasswordChange(resetUserId, newPasswordHash);
|
boolean sent = smsAuthService.sendAuthCode(userInfo, authCode);
|
||||||
|
if (!sent) {
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(false)
|
||||||
|
.message(toAlertMessage("인증번호 발송에 실패했습니다. 잠시 후 다시 시도해주세요."))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "S", localeMessage
|
// 실제 저장은 SMS 인증 성공 후(commitPendingPasswordChange)에 수행한다.
|
||||||
.getString("login.pwdchanged"));
|
smsAuthService.setupPasswordChangeAuthSession(session, userInfo, newPasswordHash, authCode);
|
||||||
|
|
||||||
return ChangePasswordResponseDto.builder()
|
return ChangePasswordResponseDto.builder()
|
||||||
.success(true)
|
.success(true)
|
||||||
.message(toAlertMessage(localeMessage.getString("login.pwdchanged")))
|
.smsAuthRequired(true)
|
||||||
.redirectUrl(request.getContextPath() + "/emergency.jsp")
|
.maskedPhone(smsAuthService.maskPhoneNumber(userInfo.getCphnno()))
|
||||||
.build();
|
.expiresAt(smsAuthService.getExpiresAt(session))
|
||||||
|
.resendableAt(smsAuthService.getResendableAt(session))
|
||||||
|
.smsTestHint(smsAuthService.getFixedModeHintText())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMS 인증 비활성화 또는 비상모드 - 기존처럼 즉시 반영
|
||||||
|
return applyPasswordChange(request, userInfo, newPasswordHash);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("changePassword Error ", e);
|
logger.error("changePassword Error ", e);
|
||||||
@@ -933,6 +954,52 @@ public class MainController implements InterceptorSkipController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비밀번호 변경 SMS 인증 성공 후 호출. 세션에 대기 중인 새 비밀번호를 실제로 반영한다.
|
||||||
|
* ({@code SmsAuthController}에서 인증번호 검증 성공 시 호출)
|
||||||
|
*/
|
||||||
|
public ChangePasswordResponseDto commitPendingPasswordChange(HttpServletRequest request, HttpSession session) {
|
||||||
|
UserInfo userInfo = smsAuthService.getUserInfoFromSession(session);
|
||||||
|
String pendingPasswordHash = smsAuthService.getPendingPasswordHashFromSession(session);
|
||||||
|
|
||||||
|
if (userInfo == null || pendingPasswordHash == null) {
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(false)
|
||||||
|
.message(toAlertMessage("인증 세션이 만료되었습니다. 다시 시도해주세요."))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ChangePasswordResponseDto result = applyPasswordChange(request, userInfo, pendingPasswordHash);
|
||||||
|
smsAuthService.clearAuthSession(session);
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("commitPendingPasswordChange Error ", e);
|
||||||
|
smsAuthService.clearAuthSession(session);
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(false)
|
||||||
|
.message(toAlertMessage(localeMessage.getString("login.pwdchangefail")))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 비밀번호 저장 + 변경 이력 기록 (SMS 인증 완료 여부와 무관하게 최종 반영 단계에서 공통 호출)
|
||||||
|
private ChangePasswordResponseDto applyPasswordChange(HttpServletRequest request, UserInfo userInfo, String newPasswordHash) {
|
||||||
|
userInfo.setPassword(newPasswordHash);
|
||||||
|
userInfoService.save(userInfo);
|
||||||
|
|
||||||
|
userPasswordHistoryService.recordPasswordChange(userInfo.getUserid(), newPasswordHash);
|
||||||
|
|
||||||
|
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "S", localeMessage
|
||||||
|
.getString("login.pwdchanged"));
|
||||||
|
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(true)
|
||||||
|
.message(toAlertMessage(localeMessage.getString("login.pwdchanged")))
|
||||||
|
.redirectUrl(request.getContextPath() + "/emergency.jsp")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private static final int PASSWORD_PATTERN_CHECK_LENGTH = 3;
|
private static final int PASSWORD_PATTERN_CHECK_LENGTH = 3;
|
||||||
|
|
||||||
private static final String[] KEYBOARD_SEQUENTIAL_ROWS = {
|
private static final String[] KEYBOARD_SEQUENTIAL_ROWS = {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.eactive.eai.rms.ext.djb.smsauth;
|
|||||||
|
|
||||||
import com.eactive.apim.portal.user.entity.UserInfo;
|
import com.eactive.apim.portal.user.entity.UserInfo;
|
||||||
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
|
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
|
||||||
|
import com.eactive.eai.rms.common.login.ChangePasswordResponseDto;
|
||||||
import com.eactive.eai.rms.common.login.LoginVo;
|
import com.eactive.eai.rms.common.login.LoginVo;
|
||||||
import com.eactive.eai.rms.common.login.MainController;
|
import com.eactive.eai.rms.common.login.MainController;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -70,6 +71,7 @@ public class SmsAuthController implements InterceptorSkipController {
|
|||||||
.maskedPhone(getSmsAuthService().maskPhoneNumber(userInfo.getCphnno()))
|
.maskedPhone(getSmsAuthService().maskPhoneNumber(userInfo.getCphnno()))
|
||||||
.expiresAt(getSmsAuthService().getExpiresAt(session))
|
.expiresAt(getSmsAuthService().getExpiresAt(session))
|
||||||
.resendableAt(getSmsAuthService().getResendableAt(session))
|
.resendableAt(getSmsAuthService().getResendableAt(session))
|
||||||
|
.smsTestHint(getSmsAuthService().getFixedModeHintText())
|
||||||
.build();
|
.build();
|
||||||
} else {
|
} else {
|
||||||
return SmsAuthDto.builder()
|
return SmsAuthDto.builder()
|
||||||
@@ -119,6 +121,7 @@ public class SmsAuthController implements InterceptorSkipController {
|
|||||||
.maskedPhone(getSmsAuthService().maskPhoneNumber(userInfo.getCphnno()))
|
.maskedPhone(getSmsAuthService().maskPhoneNumber(userInfo.getCphnno()))
|
||||||
.expiresAt(getSmsAuthService().getExpiresAt(session))
|
.expiresAt(getSmsAuthService().getExpiresAt(session))
|
||||||
.resendableAt(getSmsAuthService().getResendableAt(session))
|
.resendableAt(getSmsAuthService().getResendableAt(session))
|
||||||
|
.smsTestHint(getSmsAuthService().getFixedModeHintText())
|
||||||
.build();
|
.build();
|
||||||
} else {
|
} else {
|
||||||
return SmsAuthDto.builder()
|
return SmsAuthDto.builder()
|
||||||
@@ -208,6 +211,45 @@ public class SmsAuthController implements InterceptorSkipController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMS 인증번호 검증 및 비밀번호 변경 완료
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/verify-password-change.json", method = RequestMethod.POST)
|
||||||
|
@ResponseBody
|
||||||
|
public ChangePasswordResponseDto verifyPasswordChangeAuthCode(HttpServletRequest request,
|
||||||
|
@RequestParam("authCode") String authCode) {
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
UserInfo userInfo = getSmsAuthService().getUserInfoFromSession(session);
|
||||||
|
String pendingPasswordHash = getSmsAuthService().getPendingPasswordHashFromSession(session);
|
||||||
|
|
||||||
|
if (userInfo == null || pendingPasswordHash == null) {
|
||||||
|
log.warn("SMS 인증 세션 정보 없음 - verify-password-change 요청");
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(false)
|
||||||
|
.message("인증 세션이 만료되었습니다. 다시 시도해주세요.")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSmsAuthService().isExpired(session)) {
|
||||||
|
log.warn("SMS 인증번호 만료됨(비밀번호 변경) - userId: {}", userInfo.getUserid());
|
||||||
|
getSmsAuthService().clearAuthSession(session);
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(false)
|
||||||
|
.message("인증번호가 만료되었습니다. 다시 시도해주세요.")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getSmsAuthService().validateAuthCode(session, authCode)) {
|
||||||
|
log.warn("SMS 인증번호 불일치(비밀번호 변경) - userId: {}", userInfo.getUserid());
|
||||||
|
return ChangePasswordResponseDto.builder()
|
||||||
|
.success(false)
|
||||||
|
.message("인증번호가 일치하지 않습니다.")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainController.commitPendingPasswordChange(request, session);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SMS 인증 상태 조회
|
* SMS 인증 상태 조회
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -50,4 +50,9 @@ public class SmsAuthDto {
|
|||||||
* 에러 메시지
|
* 에러 메시지
|
||||||
*/
|
*/
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* djb.sms_auth.mode=fixed 일 때만 채워지는 테스트용 인증번호 안내 문구 (예: "[테스트] 인증번호: 000000")
|
||||||
|
*/
|
||||||
|
private String smsTestHint;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public class SmsAuthService {
|
|||||||
public static final String SESSION_KEY_ISSUED_AT = "SMS_AUTH_ISSUED_AT";
|
public static final String SESSION_KEY_ISSUED_AT = "SMS_AUTH_ISSUED_AT";
|
||||||
public static final String SESSION_KEY_USER_INFO = "SMS_AUTH_USER_INFO";
|
public static final String SESSION_KEY_USER_INFO = "SMS_AUTH_USER_INFO";
|
||||||
public static final String SESSION_KEY_LOGIN_VO = "SMS_AUTH_LOGIN_VO";
|
public static final String SESSION_KEY_LOGIN_VO = "SMS_AUTH_LOGIN_VO";
|
||||||
|
public static final String SESSION_KEY_PENDING_PASSWORD_HASH = "SMS_AUTH_PENDING_PASSWORD_HASH";
|
||||||
|
|
||||||
// 시간 설정 (밀리초)
|
// 시간 설정 (밀리초)
|
||||||
public static final long EXPIRY_MS = 60_000; // 1분
|
public static final long EXPIRY_MS = 60_000; // 1분
|
||||||
@@ -130,6 +131,27 @@ public class SmsAuthService {
|
|||||||
log.debug("SMS 인증 세션 설정 완료 - userId: {}", userInfo.getUserid());
|
log.debug("SMS 인증 세션 설정 완료 - userId: {}", userInfo.getUserid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비밀번호 변경용 SMS 인증 세션 설정.
|
||||||
|
* 로그인 플로우의 {@link #setupAuthSession}과 세션 키(AUTH_CODE/ISSUED_AT/USER_INFO)를 공유하되,
|
||||||
|
* LOGIN_VO 대신 검증 성공 시 실제로 반영할 새 비밀번호 해시를 저장한다.
|
||||||
|
*/
|
||||||
|
public void setupPasswordChangeAuthSession(HttpSession session, UserInfo userInfo, String pendingPasswordHash, String authCode) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
session.setAttribute(SESSION_KEY_AUTH_CODE, authCode);
|
||||||
|
session.setAttribute(SESSION_KEY_ISSUED_AT, now);
|
||||||
|
session.setAttribute(SESSION_KEY_USER_INFO, userInfo);
|
||||||
|
session.setAttribute(SESSION_KEY_PENDING_PASSWORD_HASH, pendingPasswordHash);
|
||||||
|
log.debug("SMS 인증 세션(비밀번호 변경용) 설정 완료 - userId: {}", userInfo.getUserid());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 세션에서 대기 중인 새 비밀번호 해시 조회 (비밀번호 변경 SMS 인증용)
|
||||||
|
*/
|
||||||
|
public String getPendingPasswordHashFromSession(HttpSession session) {
|
||||||
|
return (String) session.getAttribute(SESSION_KEY_PENDING_PASSWORD_HASH);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 인증번호 검증
|
* 인증번호 검증
|
||||||
*/
|
*/
|
||||||
@@ -224,6 +246,7 @@ public class SmsAuthService {
|
|||||||
session.removeAttribute(SESSION_KEY_ISSUED_AT);
|
session.removeAttribute(SESSION_KEY_ISSUED_AT);
|
||||||
session.removeAttribute(SESSION_KEY_USER_INFO);
|
session.removeAttribute(SESSION_KEY_USER_INFO);
|
||||||
session.removeAttribute(SESSION_KEY_LOGIN_VO);
|
session.removeAttribute(SESSION_KEY_LOGIN_VO);
|
||||||
|
session.removeAttribute(SESSION_KEY_PENDING_PASSWORD_HASH);
|
||||||
log.debug("SMS 인증 세션 정보 클리어 완료");
|
log.debug("SMS 인증 세션 정보 클리어 완료");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,4 +289,17 @@ public class SmsAuthService {
|
|||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return getProperty().isSmsAuthEnabled();
|
return getProperty().isSmsAuthEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fixed 모드(djb.sms_auth.mode=fixed)일 때 화면에 표시할 테스트용 안내 문구.
|
||||||
|
* 실제 SMS 발송 없이도 개발/테스트 환경에서 인증번호를 알 수 있도록 노출한다.
|
||||||
|
* fixed 모드가 아니면 null (화면에서는 힌트를 표시하지 않음).
|
||||||
|
*/
|
||||||
|
public String getFixedModeHintText() {
|
||||||
|
DjbProperty prop = getProperty();
|
||||||
|
if (!MODE_FIXED.equalsIgnoreCase(prop.getSmsAuthMode())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return "[테스트] 인증번호: " + prop.getSmsAuthFixedValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user