취약점 점검 수정
This commit is contained in:
@@ -175,6 +175,6 @@
|
||||
</jsp-config>
|
||||
|
||||
<session-config>
|
||||
<session-timeout>60</session-timeout>
|
||||
<session-timeout>10</session-timeout>
|
||||
</session-config>
|
||||
</web-app>
|
||||
@@ -145,6 +145,6 @@
|
||||
</jsp-config>
|
||||
|
||||
<session-config>
|
||||
<session-timeout>60</session-timeout>
|
||||
<session-timeout>10</session-timeout>
|
||||
</session-config>
|
||||
</web-app>
|
||||
@@ -102,7 +102,7 @@
|
||||
window.location.href = response.redirectUrl;
|
||||
} else {
|
||||
// 로그인 실패
|
||||
alert(response.errorMessage || '로그인에 실패했습니다.');
|
||||
alert('로그인에 실패했습니다');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
@@ -262,7 +262,36 @@
|
||||
|
||||
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) {
|
||||
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 ) {
|
||||
@@ -276,12 +305,12 @@
|
||||
});
|
||||
$("input[name=changePassword]").keydown(function(event){
|
||||
if ( event.which == 13 ) {
|
||||
$('#modalLoginForm').submit();
|
||||
submitChangePassword();
|
||||
}
|
||||
});
|
||||
$("input[name=confirmPassword]").keydown(function(event){
|
||||
if ( event.which == 13 ) {
|
||||
$('#modalLoginForm').submit();
|
||||
submitChangePassword();
|
||||
}
|
||||
});
|
||||
// Password Change 링크로 직접 열 때만 초기화 (JS에서 modal('show') 호출 시 relatedTarget 없음)
|
||||
@@ -387,7 +416,7 @@
|
||||
<span aria-hidden="true">X</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="modalLoginForm" action="<c:url value="/changePassword.do"/>" method="post" onsubmit="return checkPwd()" novalidate>
|
||||
<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">
|
||||
@@ -405,7 +434,8 @@
|
||||
<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">
|
||||
※ 7글자 이상 & 영문/숫자/특수문자 2종류 이상
|
||||
※ 7글자 이상 & 영문/숫자/특수문자 2종류 이상<br/>
|
||||
※ 연속된 숫자/문자(예: 123, abc, qwer), 동일 문자 3자 이상 반복 사용 불가
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -269,7 +269,10 @@ public interface MonitoringContext {
|
||||
|
||||
// 최대 로그인 실패 허용횟수 (0이면 계정 잠금 미적용)
|
||||
public static final String RMS_PASSWORD_FAIL_COUNT = "rms.password.fail.count";
|
||||
|
||||
|
||||
// 비밀번호 변경 시 재사용을 금지할 최근 이력 개수
|
||||
public static final String RMS_PASSWORD_HISTORY_COUNT = "rms.password.history.count";
|
||||
|
||||
// API 상태변화 스윙챗 발송여부
|
||||
public static final String DJB_UMS_MESSENGER_APIMONITOR_ENABLED = "djb.ums.messenger.api-monitor.enabled";
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.eactive.eai.rms.common.login;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ChangePasswordResponseDto {
|
||||
|
||||
/**
|
||||
* 비밀번호 변경 성공 여부
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 사용자에게 alert로 표시할 메시지
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 성공 시 이동할 URL
|
||||
*/
|
||||
private String redirectUrl;
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import com.eactive.apim.portal.user.entity.UserInfo;
|
||||
import com.eactive.eai.rms.data.entity.man.user.UserInfoService;
|
||||
import com.eactive.eai.rms.data.entity.man.user.UserLoginHistory;
|
||||
import com.eactive.eai.rms.data.entity.man.user.UserLoginHistoryService;
|
||||
import com.eactive.eai.rms.data.entity.man.user.AdminUserPasswordHistoryService;
|
||||
import com.eactive.eai.rms.data.entity.man.user.UserRole;
|
||||
import com.eactive.eai.rms.data.entity.man.user.UserRoleService;
|
||||
import com.eactive.eai.rms.data.entity.man.user.UserServiceTypeService;
|
||||
@@ -62,10 +63,10 @@ public class MainController implements InterceptorSkipController {
|
||||
private static final String USER_NOT_FOUND_KEY_2 = "login.useridnotfound2";
|
||||
|
||||
private static final String REDIRECT_LOGIN_URL = "redirect:/emergency.jsp";
|
||||
private static final String REDIRECT_CHANGE_PASSWORD_URL = "redirect:/emergency.jsp";
|
||||
|
||||
private static final String USER_STATUS_LOCKED = "2";
|
||||
private static final int DEFAULT_MAX_LOGIN_FAIL_COUNT = 5;
|
||||
private static final int DEFAULT_PASSWORD_HISTORY_COUNT = 5;
|
||||
|
||||
private final LocaleMessage localeMessage;
|
||||
private final MonitoringContext monitoringContext;
|
||||
@@ -76,6 +77,7 @@ public class MainController implements InterceptorSkipController {
|
||||
private final LoginService loginService;
|
||||
private final MonitoringPropertyService monitoringPropertyService;
|
||||
private final UserLoginHistoryService userLoginHistoryService;
|
||||
private final AdminUserPasswordHistoryService userPasswordHistoryService;
|
||||
private final SmsAuthService smsAuthService;
|
||||
|
||||
@Autowired
|
||||
@@ -87,6 +89,7 @@ public class MainController implements InterceptorSkipController {
|
||||
UserServiceTypeService userServiceTypeService,
|
||||
MonitoringPropertyService monitoringPropertyService,
|
||||
UserLoginHistoryService userLoginHistoryService,
|
||||
AdminUserPasswordHistoryService userPasswordHistoryService,
|
||||
SmsAuthService smsAuthService) {
|
||||
this.localeMessage = localeMessage;
|
||||
this.monitoringContext = monitoringContext;
|
||||
@@ -96,6 +99,7 @@ public class MainController implements InterceptorSkipController {
|
||||
this.userServiceTypeService = userServiceTypeService;
|
||||
this.monitoringPropertyService = monitoringPropertyService;
|
||||
this.userLoginHistoryService = userLoginHistoryService;
|
||||
this.userPasswordHistoryService = userPasswordHistoryService;
|
||||
this.smsAuthService = smsAuthService;
|
||||
}
|
||||
|
||||
@@ -453,6 +457,12 @@ public class MainController implements InterceptorSkipController {
|
||||
MonitoringContext.RMS_PASSWORD_FAIL_COUNT, DEFAULT_MAX_LOGIN_FAIL_COUNT);
|
||||
}
|
||||
|
||||
// 비밀번호 재사용 금지 대상 최근 이력 개수 조회 (rms.password.history.count)
|
||||
private int getPasswordHistoryCount() {
|
||||
return monitoringContext.getIntProperty(
|
||||
MonitoringContext.RMS_PASSWORD_HISTORY_COUNT, DEFAULT_PASSWORD_HISTORY_COUNT);
|
||||
}
|
||||
|
||||
// 로그인 실패 횟수 증가, 임계치 도달 시 계정 잠금
|
||||
private int increaseLoginFailCount(UserInfo userInfo) {
|
||||
int failCount = (userInfo.getLoginfailcount() == null ? 0 : userInfo.getLoginfailcount()) + 1;
|
||||
@@ -824,7 +834,8 @@ public class MainController implements InterceptorSkipController {
|
||||
}
|
||||
|
||||
@RequestMapping("/changePassword.do")
|
||||
public String changePassword(HttpServletRequest request,
|
||||
@ResponseBody
|
||||
public ChangePasswordResponseDto changePassword(HttpServletRequest request,
|
||||
String changePassword, String confirmPassword,
|
||||
String resetUserId, String resetPassword) {
|
||||
HttpSession session = request.getSession();
|
||||
@@ -842,9 +853,10 @@ public class MainController implements InterceptorSkipController {
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||
localeMessage
|
||||
.getString(USER_NOT_FOUND_KEY_1));
|
||||
session.setAttribute(RESULT_MSG, localeMessage
|
||||
.getString(USER_NOT_FOUND_KEY_1));
|
||||
return REDIRECT_LOGIN_URL;
|
||||
return ChangePasswordResponseDto.builder()
|
||||
.success(false)
|
||||
.message(toAlertMessage(localeMessage.getString(USER_NOT_FOUND_KEY_1)))
|
||||
.build();
|
||||
}
|
||||
|
||||
UserInfo userInfo = userInfoOptional.get();
|
||||
@@ -854,9 +866,10 @@ public class MainController implements InterceptorSkipController {
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||
localeMessage
|
||||
.getString("login.pwdmismatch1"));
|
||||
session.setAttribute(RESULT_MSG, localeMessage
|
||||
.getString("login.pwdmismatch2"));
|
||||
return REDIRECT_CHANGE_PASSWORD_URL;
|
||||
return ChangePasswordResponseDto.builder()
|
||||
.success(false)
|
||||
.message(toAlertMessage(localeMessage.getString("login.pwdmismatch2")))
|
||||
.build();
|
||||
}
|
||||
|
||||
// 2. 비밀번호 Validation
|
||||
@@ -864,48 +877,80 @@ public class MainController implements InterceptorSkipController {
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||
localeMessage
|
||||
.getString("login.pwdmismatch3"));
|
||||
session.setAttribute(RESULT_MSG, localeMessage
|
||||
.getString("login.pwdmismatch4"));
|
||||
return REDIRECT_CHANGE_PASSWORD_URL;
|
||||
return ChangePasswordResponseDto.builder()
|
||||
.success(false)
|
||||
.message(toAlertMessage(localeMessage.getString("login.pwdmismatch4")))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
// 3. 비밀번호 체크
|
||||
if (!userInfo.getPassword().equals(DamoManager.getInstance().hash(DamoManager.SHA256,resetPassword))
|
||||
|| !userInfo.getUserid().equals(resetUserId)) {
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||
localeMessage
|
||||
.getString("login.pwdmismatch5"));
|
||||
session.setAttribute(RESULT_MSG, localeMessage
|
||||
.getString("login.pwdmismatch6"));
|
||||
return REDIRECT_CHANGE_PASSWORD_URL;
|
||||
return ChangePasswordResponseDto.builder()
|
||||
.success(false)
|
||||
.message(toAlertMessage(localeMessage.getString("login.pwdmismatch6")))
|
||||
.build();
|
||||
}
|
||||
|
||||
// 4. 비밀번호 변경
|
||||
userInfo.setPassword(DamoManager.getInstance().hash(DamoManager.SHA256,changePassword));
|
||||
// 4. 최근 사용한 비밀번호 재사용 금지
|
||||
String newPasswordHash = DamoManager.getInstance().hash(DamoManager.SHA256, changePassword);
|
||||
if (userPasswordHistoryService.isPasswordReused(resetUserId, newPasswordHash, getPasswordHistoryCount())) {
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||
localeMessage
|
||||
.getString("login.pwdreused1"));
|
||||
return ChangePasswordResponseDto.builder()
|
||||
.success(false)
|
||||
.message(toAlertMessage(localeMessage.getString("login.pwdreused2")))
|
||||
.build();
|
||||
}
|
||||
|
||||
// 5. 비밀번호 변경
|
||||
userInfo.setPassword(newPasswordHash);
|
||||
userInfoService.save(userInfo);
|
||||
|
||||
// 6. 비밀번호 변경 이력 저장 (재사용 금지 검증용)
|
||||
userPasswordHistoryService.recordPasswordChange(resetUserId, newPasswordHash);
|
||||
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "S", localeMessage
|
||||
.getString("login.pwdchanged"));
|
||||
session.setAttribute(RESULT_MSG, localeMessage
|
||||
.getString("login.pwdchanged"));
|
||||
logger.debug("\n\n\n\n===================" + localeMessage
|
||||
.getString("login.pwdchanged"));
|
||||
return REDIRECT_LOGIN_URL;
|
||||
|
||||
return ChangePasswordResponseDto.builder()
|
||||
.success(true)
|
||||
.message(toAlertMessage(localeMessage.getString("login.pwdchanged")))
|
||||
.redirectUrl(request.getContextPath() + "/emergency.jsp")
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("changePassword Error ", e);
|
||||
return "redirect:/common/errors/errorLogon.jsp";
|
||||
return ChangePasswordResponseDto.builder()
|
||||
.success(false)
|
||||
.message(toAlertMessage(localeMessage.getString("login.pwdchangefail")))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final int PASSWORD_PATTERN_CHECK_LENGTH = 3;
|
||||
|
||||
private static final String[] KEYBOARD_SEQUENTIAL_ROWS = {
|
||||
"1234567890",
|
||||
"!@#$%^&*()",
|
||||
"qwertyuiop",
|
||||
"asdfghjkl",
|
||||
"zxcvbnm"
|
||||
};
|
||||
|
||||
private boolean isPasswordValid(String password) {
|
||||
|
||||
return password != null && password.getBytes().length == password
|
||||
.length() && password.getBytes().length >= 7
|
||||
&& isCombination(password)
|
||||
&& !isRepeatPassword(password, 5)
|
||||
&& !isSerialPassword(password, 5);
|
||||
&& !isRepeatPassword(password, PASSWORD_PATTERN_CHECK_LENGTH)
|
||||
&& !isSerialPassword(password, PASSWORD_PATTERN_CHECK_LENGTH)
|
||||
&& !isKeyboardSequentialPassword(password, PASSWORD_PATTERN_CHECK_LENGTH);
|
||||
}
|
||||
|
||||
private boolean isRepeatPassword(String password, int count) {
|
||||
@@ -930,21 +975,41 @@ public class MainController implements InterceptorSkipController {
|
||||
}
|
||||
|
||||
private boolean isSerialPassword(String password, int count) {
|
||||
int preData = 0;
|
||||
int serialcount = 1;
|
||||
int ascCount = 1;
|
||||
int descCount = 1;
|
||||
|
||||
for (int i = 0; i < password.length(); i++) {
|
||||
for (int i = 1; i < password.length(); i++) {
|
||||
|
||||
char prev = password.charAt(i - 1);
|
||||
char curr = password.charAt(i);
|
||||
|
||||
ascCount = (curr == prev + 1) ? ascCount + 1 : 1;
|
||||
descCount = (curr == prev - 1) ? descCount + 1 : 1;
|
||||
|
||||
if (ascCount >= count || descCount >= count) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 키보드 자판 배열상 연속된 문자열(qwer, asdf 등, 역순 포함) 사용 여부 체크
|
||||
private boolean isKeyboardSequentialPassword(String password, int count) {
|
||||
String lowerPassword = password.toLowerCase();
|
||||
|
||||
for (String row : KEYBOARD_SEQUENTIAL_ROWS) {
|
||||
|
||||
for (int i = 0; i <= row.length() - count; i++) {
|
||||
|
||||
String forward = row.substring(i, i + count);
|
||||
String backward = new StringBuilder(forward).reverse().toString();
|
||||
|
||||
if (lowerPassword.contains(forward) || lowerPassword.contains(backward)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (preData + 1 == password.charAt(i)) {
|
||||
serialcount++;
|
||||
} else {
|
||||
serialcount = 1;
|
||||
}
|
||||
|
||||
preData = password.charAt(i);
|
||||
|
||||
if (serialcount == count) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.eactive.eai.rms.data.entity.man.user;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import com.eactive.eai.data.entity.AbstractEntity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* 관리콘솔(TSEAIRM02) 로그인 사용자의 비밀번호 변경 이력 (TSEAIRM08).
|
||||
* 포털 API 소비자 비밀번호 이력({@code com.eactive.apim.portal.portaluser.entity.UserPasswordHistory})과는 별개 테이블/도메인이다.
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
|
||||
@Entity
|
||||
@Table(name = "TSEAIRM08")
|
||||
@org.hibernate.annotations.Table(appliesTo = "TSEAIRM08", comment = "관리콘솔 사용자 비밀번호 변경 이력")
|
||||
public class AdminUserPasswordHistory extends AbstractEntity<Integer> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GenericGenerator(
|
||||
name = "TSEAIRM08_GEN",
|
||||
strategy = "com.eactive.eai.rms.data.jpa.SchemaPrefixedSequenceGenerator",
|
||||
parameters = {
|
||||
@org.hibernate.annotations.Parameter(name = "sequence_name", value = "SEQ_TSEAIRM08"),
|
||||
@org.hibernate.annotations.Parameter(name = "initial_value", value = "1"),
|
||||
@org.hibernate.annotations.Parameter(name = "increment_size", value = "1")
|
||||
}
|
||||
)
|
||||
@GeneratedValue(
|
||||
strategy = GenerationType.SEQUENCE,
|
||||
generator = "TSEAIRM08_GEN"
|
||||
)
|
||||
@Column(name = "ID", unique = true, nullable = false)
|
||||
@Comment("비밀번호 변경 이력 식별 ID")
|
||||
private int pwdHistId;
|
||||
|
||||
@Column(name = "USERID", length = 255, nullable = false)
|
||||
@Comment("사용자 식별자(사번)")
|
||||
private String userId;
|
||||
|
||||
@Column(name = "PASSWORD_HASH", length = 255, nullable = false)
|
||||
@Comment("변경된 비밀번호 해시값")
|
||||
private String passwordHash;
|
||||
|
||||
@Column(name = "CHANGE_DATE", length = 14)
|
||||
@Comment("비밀번호 변경 일시 (yyyyMMddHHmmss)")
|
||||
private String changeDate;
|
||||
|
||||
@Column(name = "CREATED_BY", length = 200)
|
||||
@Comment("등록자 식별자")
|
||||
private String createdBy;
|
||||
|
||||
@Column(name = "CREATED_DATE", length = 14)
|
||||
@Comment("레코드 생성 일시 (yyyyMMddHHmmss)")
|
||||
private String createdDate;
|
||||
|
||||
@Override
|
||||
public @NonNull Integer getId() {
|
||||
return pwdHistId;
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.eactive.eai.rms.data.entity.man.user;
|
||||
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import com.eactive.eai.data.jpa.BaseRepository;
|
||||
|
||||
@EMSDataSource
|
||||
public interface AdminUserPasswordHistoryRepository extends BaseRepository<AdminUserPasswordHistory, Integer> {
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.eactive.eai.rms.data.entity.man.user;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.eactive.eai.data.converter.LocalDateTimeFormatters;
|
||||
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
|
||||
@Service
|
||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||
public class AdminUserPasswordHistoryService
|
||||
extends AbstractEMSDataSerivce<AdminUserPasswordHistory, Integer, AdminUserPasswordHistoryRepository> {
|
||||
|
||||
/** 사용자의 최근 비밀번호 이력을 최신순으로 historyCount건 조회 */
|
||||
public List<AdminUserPasswordHistory> findRecent(String userId, int historyCount) {
|
||||
BooleanExpression predicate = QAdminUserPasswordHistory.adminUserPasswordHistory.userId.eq(userId);
|
||||
Pageable pageable = PageRequest.of(0, historyCount, Sort.by(Sort.Direction.DESC, "pwdHistId"));
|
||||
|
||||
return repository.findAll(predicate, pageable).getContent();
|
||||
}
|
||||
|
||||
/** 최근 historyCount건 이내에 동일한 비밀번호(해시값)가 있었는지 확인 */
|
||||
public boolean isPasswordReused(String userId, String newPasswordHash, int historyCount) {
|
||||
if (historyCount <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return findRecent(userId, historyCount).stream()
|
||||
.anyMatch(history -> history.getPasswordHash().equals(newPasswordHash));
|
||||
}
|
||||
|
||||
/** 비밀번호 변경 이력 저장 */
|
||||
public void recordPasswordChange(String userId, String passwordHash) {
|
||||
String now = LocalDateTime.now().format(LocalDateTimeFormatters.FORMATTER_YYYYMMDDHHMMSS_14);
|
||||
|
||||
AdminUserPasswordHistory history = new AdminUserPasswordHistory();
|
||||
history.setUserId(userId);
|
||||
history.setPasswordHash(passwordHash);
|
||||
history.setChangeDate(now);
|
||||
history.setCreatedBy(userId);
|
||||
history.setCreatedDate(now);
|
||||
|
||||
save(history);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1215,9 +1215,12 @@ login.pwdchanged = Your password has been changed.
|
||||
login.pwdmismatch1 = Your change password is different from your verification password.
|
||||
login.pwdmismatch2 = Your change password is different from your verification password. <BR/> Please check your login information.
|
||||
login.pwdmismatch3 = The change password does not match the rule.
|
||||
login.pwdmismatch4 = The change password does not match the rule. <BR/> Please check your login information.
|
||||
login.pwdmismatch4 = The change password does not match the rule.
|
||||
login.pwdmismatch5 = Your password is different.
|
||||
login.pwdmismatch6 = Your password is different. <BR/> Please check your login information.
|
||||
login.pwdmismatch6 = Your password is different.
|
||||
login.pwdreused1 = You cannot reuse a recently used password.
|
||||
login.pwdreused2 = You cannot reuse a recently used password. <BR/> Please enter a different password.
|
||||
login.pwdchangefail = An error occurred while changing the password.
|
||||
login.pwdnotchanged1 = You did not change your password after resetting your password. Please change your password and login.
|
||||
login.pwdnotchanged2 = You did not change your password after resetting your password.<BR/>Please change your password and login.
|
||||
login.seedencriptfail = Seed encryption failed. <BR/> Please sign in again.
|
||||
|
||||
@@ -1360,9 +1360,12 @@ login.pwdchanged = Your password has been changed.
|
||||
login.pwdmismatch1 = Your change password is different from your verification password.
|
||||
login.pwdmismatch2 = Your change password is different from your verification password. <BR/> Please check your login information.
|
||||
login.pwdmismatch3 = The change password does not match the rule.
|
||||
login.pwdmismatch4 = The change password does not match the rule. <BR/> Please check your login information.
|
||||
login.pwdmismatch4 = The change password does not match the rule.
|
||||
login.pwdmismatch5 = Your password is different.
|
||||
login.pwdmismatch6 = Your password is different. <BR/> Please check your login information.
|
||||
login.pwdmismatch6 = Your password is different.
|
||||
login.pwdreused1 = You cannot reuse a recently used password.
|
||||
login.pwdreused2 = You cannot reuse a recently used password. <BR/> Please enter a different password.
|
||||
login.pwdchangefail = An error occurred while changing the password.
|
||||
login.pwdnotchanged1 = You did not change your password after resetting your password. Please change your password and login.
|
||||
login.pwdnotchanged2 = You did not change your password after resetting your password.<BR/>Please change your password and login.
|
||||
login.seedencriptfail = Seed encryption failed. <BR/> Please sign in again.
|
||||
|
||||
@@ -1440,9 +1440,12 @@ login.pwdchanged = \uBE44\uBC00\uBC88\uD638\uAC00 \uBCC0\uA
|
||||
login.pwdmismatch1 = \uBCC0\uACBD \uBE44\uBC00\uBC88\uD638 \uC640 \uD655\uC778 \uBE44\uBC00\uBC88\uD638\uAC00 \uB2E4\uB985\uB2C8\uB2E4.
|
||||
login.pwdmismatch2 = \uBCC0\uACBD \uBE44\uBC00\uBC88\uD638 \uC640 \uD655\uC778 \uBE44\uBC00\uBC88\uD638\uAC00 \uB2E4\uB985\uB2C8\uB2E4.<BR/> \uB85C\uADF8\uC778\uC815\uBCF4\uB97C \uD655\uC778\uD558\uC138\uC694.
|
||||
login.pwdmismatch3 = \uBCC0\uACBD\uBE44\uBC00\uBC88\uD638\uAC00 \uB8F0\uC5D0 \uC548\uB9DE\uC2B5\uB2C8\uB2E4.
|
||||
login.pwdmismatch4 = \uBCC0\uACBD\uBE44\uBC00\uBC88\uD638\uAC00 \uB8F0\uC5D0 \uC548\uB9DE\uC2B5\uB2C8\uB2E4.<BR/> \uB85C\uADF8\uC778\uC815\uBCF4\uB97C \uD655\uC778\uD558\uC138\uC694.
|
||||
login.pwdmismatch4 = \uBCC0\uACBD\uBE44\uBC00\uBC88\uD638\uAC00 \uB8F0\uC5D0 \uC548\uB9DE\uC2B5\uB2C8\uB2E4.
|
||||
login.pwdmismatch5 = \uBCC0\uACBD\uC804 \uBE44\uBC00\uBC88\uD638\uAC00 \uB2E4\uB985\uB2C8\uB2E4.
|
||||
login.pwdmismatch6 = \uBCC0\uACBD\uC804 \uBE44\uBC00\uBC88\uD638\uAC00 \uB2E4\uB985\uB2C8\uB2E4.<BR/> \uB85C\uADF8\uC778\uC815\uBCF4\uB97C \uD655\uC778\uD558\uC138\uC694.
|
||||
login.pwdmismatch6 = \uBCC0\uACBD\uC804 \uBE44\uBC00\uBC88\uD638\uAC00 \uB2E4\uB985\uB2C8\uB2E4.
|
||||
login.pwdreused1 = \uCD5C\uADFC \uC0AC\uC6A9\uD55C \uBE44\uBC00\uBC88\uD638\uB294 \uB2E4\uC2DC \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
||||
login.pwdreused2 = \uCD5C\uADFC \uC0AC\uC6A9\uD55C \uBE44\uBC00\uBC88\uD638\uB294 \uB2E4\uC2DC \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.<BR/> \uB2E4\uB978 \uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD558\uC138\uC694.
|
||||
login.pwdchangefail = \uBE44\uBC00\uBC88\uD638 \uBCC0\uACBD \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.
|
||||
login.pwdnotchanged1 = \uBE44\uBC00\uBC88\uD638 \uCD08\uAE30\uD654\uD6C4 \uBE44\uBC00\uBC88\uD638 \uBCC0\uACBD\uC744 \uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.\uBE44\uBC00\uBC88\uD638\uB97C \uBCC0\uACBD\uD558\uC2DC\uACE0 \uB85C\uADF8\uC778 \uD558\uC138\uC694
|
||||
login.pwdnotchanged2 = \uBE44\uBC00\uBC88\uD638 \uCD08\uAE30\uD654\uD6C4 \uBE44\uBC00\uBC88\uD638 \uBCC0\uACBD\uC744 \uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.\uBE44\uBC00\uBC88\uD638\uB97C \uBCC0\uACBD\uD558\uC2DC\uACE0 \uB85C\uADF8\uC778 \uD558\uC138\uC694.<BR/> \uB85C\uADF8\uC778\uC815\uBCF4\uB97C \uD655\uC778\uD558\uC138\uC694.
|
||||
login.seedencriptfail = Seed \uC554\uD638\uD654\uC5D0 \uC2E4\uD328 \uD558\uC600\uC2B5\uB2C8\uB2E4.<BR/> \uB2E4\uC2DC \uB85C\uADF8\uC778\uD558\uC138\uC694.
|
||||
|
||||
Reference in New Issue
Block a user