Damo 적용 준비 코드
eapim-portal CI (from elink-portal-common) / build (push) Has been cancelled

- KjbNotRnnoJpaAttributeConverter 삭제 및 PersonalDataEncryptConverter 추가
- damo-manager 지원을 위한 JSP 추가 및 레거시 암호화 마이그레이션 컨트롤러 구현
- 기존 엔티티 암호화 로직 변경 및 build.gradle에 damo-manager 의존성 추가
This commit is contained in:
Rinjae
2026-06-22 16:29:45 +09:00
parent 09013156dc
commit de262cec7b
11 changed files with 154 additions and 162 deletions
+2
View File
@@ -114,6 +114,8 @@ dependencies {
api group: 'commons-io', name: 'commons-io', version: '2.11.0' api group: 'commons-io', name: 'commons-io', version: '2.11.0'
compileOnly files('libs/damo-manager.jar')
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'ch.qos.logback:logback-classic:1.2.10'
Binary file not shown.
@@ -1,7 +1,7 @@
package com.eactive.apim.portal.common.entity; package com.eactive.apim.portal.common.entity;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14; import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
import kjb.safedb.KjbNotRnnoJpaAttributeConverter;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedBy;
@@ -34,7 +34,7 @@ public abstract class Auditable {
* 최초등록자ID * 최초등록자ID
*/ */
@Column(name = "created_by") @Column(name = "created_by")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
@CreatedBy @CreatedBy
private String createdBy; private String createdBy;
@@ -50,7 +50,7 @@ public abstract class Auditable {
* 최종수정자ID * 최종수정자ID
*/ */
@Column(name = "LAST_MODIFIED_BY") @Column(name = "LAST_MODIFIED_BY")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
@LastModifiedBy @LastModifiedBy
private String lastModifiedBy; private String lastModifiedBy;
@@ -0,0 +1,115 @@
package com.eactive.apim.portal.jpa;
import com.eactive.ext.djb.DamoManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Slf4j
@Converter
public class PersonalDataEncryptConverter implements AttributeConverter<String, String> {
/**
* 암호화
* @param attribute the entity attribute value to be converted
* @return
*/
@Override
public String convertToDatabaseColumn(String attribute) {
String originalAttribute = attribute;
if (StringUtils.isNotEmpty(attribute)) {
attribute = DamoManager.getInstance().encrypt(attribute);
if (log.isDebugEnabled()) {
// originalAttribute 홀수 글자 마스킹
StringBuilder sb = new StringBuilder();
for (int i = 0; i < originalAttribute.length(); i++) {
if (i % 2 == 0) {
sb.append(originalAttribute.charAt(i));
} else {
sb.append("*");
}
}
originalAttribute = sb.toString();
log.debug("DB 암호화 : {} -> {}", originalAttribute, attribute);
}
}
return attribute;
}
/**
* 복호화
* @param dbData the data from the database column to be
* converted
* @return
*/
@Override
public String convertToEntityAttribute(String dbData) {
String dbDataOriginal = dbData;
if (StringUtils.isNotEmpty(dbData)) {
if (this.isEncrypted(dbData)) {
try {
dbData = DamoManager.getInstance().decrypt(dbData);
} catch (Exception e) {
// 복호화 실패시 원본 반환
String logData = dbData.length() <= 3 ? dbData : dbData.substring(0, 3) + "...";
log.warn("DB 복호화 실패: 복호화하지 않고 원본 반환. dbData={} (Length : {})", logData, dbData.length());
return dbData;
}
} else {
log.debug("DB 복호화 경고: Base64 형식이 아님. 복호화하지 않고 원본 반환. dbData={}", dbData);
}
}
log.debug("DB 복호화 : {} -> {}", dbDataOriginal, dbData);
return dbData;
}
/**
* 현재 damo-manager 가 bypass 모드(무변환)인지 여부.
* bypass 면 {@code encrypt}/{@code decrypt} 가 원문을 그대로 반환하므로 암호화/정규화가 no-op 이 된다.
* 레거시 평문 일괄 암호화 같은 마이그레이션 작업은 이 경우 의미가 없으므로 호출 측에서 차단해야 한다.
*/
public boolean isBypassMode() {
return DamoManager.getInstance().isBypassMode();
}
/**
* 암호화 여부 확인(base64 인코딩 여부로 판단, 또는 0-9, - 로 구성된 경우 암호화 되지 않은 걸로 판단(연락처))
* @param data
* @return
*/
private boolean isEncrypted(String data) {
if (StringUtils.isEmpty(data)) {
return false;
}
// 숫자, - 로만 구성된 경우 암호화 되지 않은 걸로 판단 (ex: 연락처)
if (data.matches("^[0-9-]+$")) {
return false;
}
data = data.trim();
// Base64 형식 체크
if (!data.matches("^[A-Za-z0-9+/]+={0,2}$")) {
return false;
}
// Base64 길이 체크 (4의 배수)
if (data.length() % 4 != 0) {
return false;
}
return true;
}
}
@@ -1,13 +1,12 @@
package com.eactive.apim.portal.portaluser.entity; package com.eactive.apim.portal.portaluser.entity;
import com.eactive.apim.portal.common.entity.Auditable; import com.eactive.apim.portal.common.entity.Auditable;
import com.eactive.apim.portal.portalorg.entity.PortalOrg; import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.ApprovalStatus; import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.ApprovalStatus;
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.RoleCode; import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.RoleCode;
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.UserStatus; import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.UserStatus;
import java.io.Serializable; import java.io.Serializable;
import kjb.safedb.KjbNotRnnoJpaAttributeConverter;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Comment; import org.hibernate.annotations.Comment;
@@ -34,7 +33,7 @@ public class PortalUser extends Auditable implements Serializable, com.eactive.e
@Column(name = "login_id", length = 200, nullable = false) @Column(name = "login_id", length = 200, nullable = false)
@Comment("로그인ID") @Comment("로그인ID")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String loginId; private String loginId;
@Column(name = "password_hash", length = 60) @Column(name = "password_hash", length = 60)
@@ -47,17 +46,17 @@ public class PortalUser extends Auditable implements Serializable, com.eactive.e
@Column(name = "phone_number", length = 50) @Column(name = "phone_number", length = 50)
@Comment("전화번호") @Comment("전화번호")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String phoneNumber; private String phoneNumber;
@Column(name = "mobile_number", length = 50) @Column(name = "mobile_number", length = 50)
@Comment("휴대폰 번호") @Comment("휴대폰 번호")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String mobileNumber; private String mobileNumber;
@Column(name = "email_addr", length = 200) @Column(name = "email_addr", length = 200)
@Comment("이메일주소") @Comment("이메일주소")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String emailAddr; private String emailAddr;
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
@@ -1,7 +1,7 @@
package com.eactive.apim.portal.portaluser.entity; package com.eactive.apim.portal.portaluser.entity;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14; import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
import kjb.safedb.KjbNotRnnoJpaAttributeConverter;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*; import javax.persistence.*;
@@ -25,7 +25,7 @@ public class TwoFactorAuth {
private String id; private String id;
@Column(name = "recipient", length = 255) @Column(name = "recipient", length = 255)
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String recipient; private String recipient;
@Column(name = "auth_number", length = 255) @Column(name = "auth_number", length = 255)
@@ -36,5 +36,12 @@ public interface PortalUserRepository extends BaseRepository<PortalUser, String>
long countByPortalOrg(PortalOrg portalOrg); long countByPortalOrg(PortalOrg portalOrg);
Optional<PortalUser> findByUserName(String userName); Optional<PortalUser> findByUserName(String userName);
// 암복
@Query(
value = "select * from ${ems.datasource.schema}.ptl_user where email_addr = :emailAddr",
nativeQuery = true
)
Optional<PortalUser> findByEmailAddrRaw(@Param("emailAddr") String emailAddr);
} }
@@ -1,9 +1,9 @@
package com.eactive.apim.portal.template.entity; package com.eactive.apim.portal.template.entity;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import com.eactive.apim.portal.portaluser.entity.PortalUser; import com.eactive.apim.portal.portaluser.entity.PortalUser;
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14; import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
import kjb.safedb.KjbNotRnnoJpaAttributeConverter;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
@@ -53,10 +53,10 @@ public class MessageRequest implements Serializable {
private String username; private String username;
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String email; private String email;
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String phone; private String phone;
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
@@ -1,28 +1,20 @@
package com.eactive.apim.portal.user.entity; package com.eactive.apim.portal.user.entity;
import java.io.Serializable; import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import java.time.LocalDateTime; import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
import com.eactive.eai.data.converter.StringTrimConverter;
import javax.persistence.Cacheable; import com.eactive.eai.data.entity.AbstractEntity;
import javax.persistence.Column; import lombok.Data;
import javax.persistence.Convert; import lombok.EqualsAndHashCode;
import javax.persistence.Entity; import lombok.NonNull;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Cache; import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Comment; import org.hibernate.annotations.Comment;
import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.annotation.LastModifiedDate;
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14; import javax.persistence.*;
import com.eactive.eai.data.converter.StringTrimConverter; import java.io.Serializable;
import com.eactive.eai.data.entity.AbstractEntity; import java.time.LocalDateTime;
import kjb.safedb.KjbNotRnnoJpaAttributeConverter;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
@Data @Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true) @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
@@ -54,7 +46,7 @@ public class UserInfo extends AbstractEntity<String> implements Serializable {
@Column(length = 40) @Column(length = 40)
@Comment("휴대폰 번호") @Comment("휴대폰 번호")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String cphnno; private String cphnno;
@Column(length = 40) @Column(length = 40)
@@ -63,7 +55,7 @@ public class UserInfo extends AbstractEntity<String> implements Serializable {
@Column(length = 100) @Column(length = 100)
@Comment("이메일 주소") @Comment("이메일 주소")
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String emad; private String emad;
@Column(length = 3) @Column(length = 3)
@@ -1,19 +1,12 @@
package com.eactive.apim.portal.user.entity; package com.eactive.apim.portal.user.entity;
import com.eactive.apim.portal.jpa.PersonalDataEncryptConverter;
import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14; import com.eactive.eai.data.converter.LocalDateTimeToStringConverter14;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import kjb.safedb.KjbNotRnnoJpaAttributeConverter;
import lombok.Data; import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
@TableGenerator( @TableGenerator(
name = "USER_LOGIN_LOG_GEN", name = "USER_LOGIN_LOG_GEN",
table = "PTL_ID", table = "PTL_ID",
@@ -32,7 +25,7 @@ public class UserLog {
private Long logId; private Long logId;
@Column(name = "login_id", nullable = false) @Column(name = "login_id", nullable = false)
@Convert(converter = KjbNotRnnoJpaAttributeConverter.class) @Convert(converter = PersonalDataEncryptConverter.class)
private String loginId; private String loginId;
@Column(name = "login_time") @Column(name = "login_time")
@@ -1,116 +0,0 @@
package kjb.safedb;
//import com.eactive.ext.kjb.safedb.KjbSafedbWrapper;
//import com.eactive.ext.kjb.safedb.SafeDBColumns;
//import com.eactive.ext.kjb.safedb.Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter
@Slf4j
public class KjbNotRnnoJpaAttributeConverter implements AttributeConverter<String, String> {
private static final Logger needFixLogger = LoggerFactory.getLogger("eapim.portal.needfix");
/**
* 암호화
* @param attribute the entity attribute value to be converted
* @return
*/
@Override
public String convertToDatabaseColumn(String attribute) {
// log.debug("DB 암호화 #1 - {} / {}", attribute, SafeDBColumns.NOT_RNNO);
// String originalAttribute = attribute;
// if (StringUtils.isNotEmpty(attribute)) {
// KjbSafedbWrapper wrapper = KjbSafedbWrapper.getInstance();
// attribute = wrapper.encryptNotRnnoString(attribute);
//
// if (log.isDebugEnabled()) {
// // originalAttribute 홀수 글자 마스킹
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < originalAttribute.length(); i++) {
// if (i % 2 == 0) {
// sb.append(originalAttribute.charAt(i));
// } else {
// sb.append("*");
// }
// }
//
// originalAttribute = sb.toString();
// log.debug("DB 암호화 #2 : {} -> {}", originalAttribute, attribute);
// }
// }
return attribute;
}
/**
* 복호화
* @param dbData the data from the database column to be
* converted
* @return
*/
@Override
public String convertToEntityAttribute(String dbData) {
// log.debug("DB 복호화 #1 - {} / {}", dbData, SafeDBColumns.NOT_RNNO);
// String dbDataOriginal = dbData;
// if (StringUtils.isNotEmpty(dbData)) {
// if (this.isEncrypted(dbData)) {
// KjbSafedbWrapper safeDBWrapper = KjbSafedbWrapper.getInstance();
// try {
// dbData = safeDBWrapper.decryptNotRnno(dbData);
// } catch (Exception e) {
// // 복호화 실패시 원본 반환
// String logData = dbData.length() <= 3 ? dbData : dbData.substring(0, 3) + "...";
// log.warn("DB 복호화 실패: 복호화하지 않고 원본 반환. dbData={} (Length : {})", logData, dbData.length());
// needFixLogger.warn("DB 복호화 실패: 복호화하지 않고 원본 반환. dbData={} (Length : {})", logData, dbData.length(), e);
// return dbData;
// }
// } else {
// log.debug("DB 복호화 경고: Base64 형식이 아님. 복호화하지 않고 원본 반환. dbData={}", dbData);
// }
// }
// log.debug("DB 복호화 #2 : {} -> {}", dbDataOriginal, dbData);
return dbData;
}
/**
* 암호화 여부 확인(base64 인코딩 여부로 판단, 또는 0-9, - 로 구성된 경우 암호화 되지 않은 걸로 판단(연락처))
* @param data
* @return
*/
private boolean isEncrypted(String data) {
// if (StringUtils.isEmpty(data)) {
// return false;
// }
//
// // 숫자, - 로만 구성된 경우 암호화 되지 않은 걸로 판단 (ex: 연락처)
// if (data.matches("^[0-9-]+$")) {
// return false;
// }
//
// data = data.trim();
//
// // Base64 형식 체크
// if (!Utils.isBase64(data)) {
// return false;
// }
//
// // Base64 길이 체크 (4의 배수)
// if (data.length() % 4 != 0) {
// return false;
// }
//
//
// return true;
return false;
}
}