광주은행 패스워드 정책 반영

This commit is contained in:
Rinjae
2025-11-07 17:05:10 +09:00
parent 0ad533497c
commit 0ddf7a343e
6 changed files with 175 additions and 8 deletions
@@ -3,8 +3,7 @@ package com.eactive.apim.portal.apps.user.dto;
import com.eactive.apim.portal.common.validator.AuthNumberMatch;
import com.eactive.apim.portal.common.validator.CellPhone;
import com.eactive.apim.portal.common.validator.PasswordMatch;
import com.eactive.apim.portal.common.validator.PasswordRule;
import com.eactive.apim.portal.common.validator.PasswordRuleForKbank;
import com.eactive.apim.portal.common.validator.PasswordRuleForKjbank;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
@@ -13,7 +12,7 @@ import org.hibernate.validator.constraints.NotEmpty;
@AuthNumberMatch(recipient = "loginId", authField = "authNumber")
@PasswordMatch(input = "password", confirm = "password2")
@Data
@PasswordRuleForKbank(password = "password", loginId = "loginId", mobile = "mobileNumber")
@PasswordRuleForKjbank(password = "password", loginId = "loginId", mobile = "mobileNumber")
public class PortalUserRegistrationDTO {
/**
@@ -1,8 +1,7 @@
package com.eactive.apim.portal.apps.user.dto;
import com.eactive.apim.portal.common.validator.PasswordMatch;
import com.eactive.apim.portal.common.validator.PasswordRule;
import com.eactive.apim.portal.common.validator.PasswordRuleForKbank;
import com.eactive.apim.portal.common.validator.PasswordRuleForKjbank;
import com.eactive.apim.portal.common.validator.UniqueId;
import com.eactive.apim.portal.portaluser.entity.UserStatus;
import lombok.Data;
@@ -14,7 +13,7 @@ import java.io.Serializable;
@PasswordMatch(input = "password", confirm = "password2")
@Data
@PasswordRuleForKbank(loginId = "userId", password = "password", mobile = "mobilePhone")
@PasswordRuleForKjbank(loginId = "userId", password = "password", mobile = "mobilePhone")
public class UserRegisterDTO implements Serializable {
@@ -1,6 +1,7 @@
package com.eactive.apim.portal.apps.user.validator;
import com.eactive.apim.portal.common.validator.PasswordRuleForKbankValidator;
import com.eactive.apim.portal.common.validator.PasswordRuleForKjbankValidator;
import org.springframework.stereotype.Component;
@Component
@@ -16,7 +17,7 @@ public class PasswordValidator {
}
public boolean isValidPassword(String password, String loginId, String mobileNumber) {
PasswordRuleForKbankValidator validator = new PasswordRuleForKbankValidator();
PasswordRuleForKjbankValidator validator = new PasswordRuleForKjbankValidator();
return validator.isValid(password, loginId, mobileNumber);
}
@@ -40,7 +40,7 @@ public class PortalAuthenticatedUser extends PortalUser implements UserDetails {
@Override
public boolean isAccountNonLocked() {
return true;
return !"Y".equalsIgnoreCase(this.getAccountLockYn());
}
@Override
@@ -0,0 +1,24 @@
package com.eactive.apim.portal.common.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Constraint(validatedBy = PasswordRuleForKjbankValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PasswordRuleForKjbank {
String message() default "비밀 번호 규칙에 부합하지 않습니다.(영문/숫자/특수문자 포함 8~20자, 아이디, 휴대전화, 3자리 이상 연속,반복 문자 불가)";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String password();
String loginId();
String mobile();
}
@@ -0,0 +1,144 @@
package com.eactive.apim.portal.common.validator;
import org.apache.commons.beanutils.PropertyUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Sungpil Hyun
*/
public class PasswordRuleForKjbankValidator implements ConstraintValidator<PasswordRuleForKjbank, Object> {
// 최소 8자, 최대 20자 상수 선언
private static final int MIN = 8;
private static final int MAX = 20;
private String password;
private String loginId;
private String mobileNumber;
// 3자리 연속 문자 정규식
private static final String SAMEPT = "(\\w)\\1\\1";
// 공백 문자 정규식
private static final String BLANKPT = "(\\s)";
@Override
public void initialize(PasswordRuleForKjbank constraintAnnotation) {
this.password = constraintAnnotation.password();
this.loginId = constraintAnnotation.loginId();
this.mobileNumber = constraintAnnotation.mobile();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
String passwordValue = null;
String loginIdValue = null;
String mobileNumberValue = null;
try {
passwordValue = (String) PropertyUtils.getProperty(value, this.password);
loginIdValue = (String) PropertyUtils.getProperty(value, this.loginId);
mobileNumberValue = (String) PropertyUtils.getProperty(value, this.mobileNumber);
} catch (Exception e) {
return false;
}
return isValid(passwordValue, loginIdValue, mobileNumberValue);
}
public boolean isValid(String password, String loginId, String mobileNumber) {
// 영어, 숫자, 특수문자 포함한 MIN to MAX 글자 정규식
String REGEX = "^((?=.*\\d)(?=.*[a-zA-Z])(?=.*[\\W]).{" + MIN + "," + MAX + "})$";
// 정규식 검사객체
Matcher matcher;
// 공백 체크
if (password == null || "".equals(password)) {
return false;
}
// ASCII 문자 비교를 위한 UpperCase
String tmpPw = password.toUpperCase();
// 문자열 길이
int strLen = tmpPw.length();
// 글자 길이 체크
if (strLen > 20 || strLen < 8) {
return false;
}
if (loginId != null && !loginId.isEmpty()) {
String[] loginParts = loginId.split("@");
if (loginParts.length > 0) {
String username = loginParts[0].toUpperCase();
if (tmpPw.contains(username)) {
return false;
}
}
}
// Mobile number validation
if (mobileNumber != null && !mobileNumber.isEmpty()) {
String[] mobileParts = mobileNumber.split("-");
for (String part : mobileParts) {
if (!part.isEmpty() && tmpPw.contains(part)) {
return false;
}
}
}
// 공백 체크
matcher = Pattern.compile(BLANKPT).matcher(tmpPw);
if (matcher.find()) {
return false;
}
// 비밀번호 정규식 체크
matcher = Pattern.compile(REGEX).matcher(tmpPw);
if (!matcher.find()) {
return false;
}
// 동일한 문자 3개 이상 체크
matcher = Pattern.compile(SAMEPT).matcher(tmpPw);
if (matcher.find()) {
return false;
}
// 연속된 문자 / 숫자 3개 이상 체크
// ASCII Char를 담을 배열 선언
int[] tmpArray = new int[strLen];
// Make Array
for (int i = 0; i < strLen; i++) {
tmpArray[i] = tmpPw.charAt(i);
}
// Validation Array
for (int i = 0; i < strLen - 2; i++) {
if (isContinuous(tmpArray[i], tmpArray[i + 2]) && isContinuous(tmpArray[i], tmpArray[i + 1], tmpArray[i + 2])) {
return false;
}
}
// Validation Complete
return true;
}
static boolean isContinuous(int first, int third) {
// 첫 글자 A-Z / 0-9
return (first > 47 && third < 58) || (first > 64 && third < 91);
}
static boolean isContinuous(int first, int second, int third) {
// 배열의 연속된 수 검사
// 3번째 글자 - 2번째 글자 = 1, 3번째 글자 - 1번째 글자 = 2
return Math.abs(third - second) == 1 && Math.abs(third - first) == 2;
}
}