EncryptionSupport 제거, ConditionSupport 추가
parent
b5cb9c9239
commit
18fa96d134
@ -1,203 +0,0 @@
|
|||||||
package cokr.xit.foundation.data;
|
|
||||||
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.egovframe.rte.fdl.cryptography.EgovPasswordEncoder;
|
|
||||||
import org.egovframe.rte.fdl.cryptography.impl.EgovARIACryptoServiceImpl;
|
|
||||||
|
|
||||||
import cokr.xit.foundation.AbstractComponent;
|
|
||||||
|
|
||||||
/**ARIA 기반의 암/복호화 서비스를 제공하는 컴포넌트
|
|
||||||
* @author mjkhan
|
|
||||||
*/
|
|
||||||
public class ARIA extends AbstractComponent {
|
|
||||||
private String
|
|
||||||
key,
|
|
||||||
algorithm;
|
|
||||||
private int blockSize;
|
|
||||||
|
|
||||||
private EgovPasswordEncoder passwordEncoder;
|
|
||||||
private EgovARIACryptoServiceImpl aria;
|
|
||||||
|
|
||||||
/**키를 반환한다.
|
|
||||||
* @return 키
|
|
||||||
*/
|
|
||||||
public String getKey() {
|
|
||||||
if (!isEmpty(key))
|
|
||||||
return key;
|
|
||||||
|
|
||||||
if (properties != null) {
|
|
||||||
return key = properties.getString("aria.key");
|
|
||||||
} else {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**키를 설정한다.
|
|
||||||
* @param key 키
|
|
||||||
* @return 현재 ARIA
|
|
||||||
*/
|
|
||||||
public ARIA setKey(String key) {
|
|
||||||
this.key = key;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**알고리즘을 반환한다. 디폴트는 SHA-256.
|
|
||||||
* @return 알고리즘
|
|
||||||
*/
|
|
||||||
public String getAlgorithm() {
|
|
||||||
if (!isEmpty(algorithm))
|
|
||||||
return algorithm;
|
|
||||||
|
|
||||||
if (properties != null) {
|
|
||||||
return algorithm = properties.getString("aria.algorithm");
|
|
||||||
} else {
|
|
||||||
return algorithm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**알고리즘을 설정한다. 디폴트는 SHA-256.
|
|
||||||
* @param algorithm 알고리즘
|
|
||||||
* @return 현재 ARIA
|
|
||||||
*/
|
|
||||||
public ARIA setAlgorithm(String algorithm) {
|
|
||||||
this.algorithm = algorithm;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**블록사이즈를 반환한다. 디폴트는 1024.
|
|
||||||
* @return 블록사이즈
|
|
||||||
*/
|
|
||||||
public int getBlockSize() {
|
|
||||||
return blockSize < 1 ? 1024 : blockSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**블록사이즈를 설정한다. 디폴트는 1024.
|
|
||||||
* @param blockSize 블록사이즈
|
|
||||||
* @return 현재 ARIA
|
|
||||||
*/
|
|
||||||
public ARIA setBlockSize(int blockSize) {
|
|
||||||
this.blockSize = blockSize;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**주어진 문자열(평문)을 암호화하여 반환한다.
|
|
||||||
* @param plain 문자열(평문)
|
|
||||||
* @return 암호화한 문자열
|
|
||||||
*/
|
|
||||||
public String encrypt(String plain) {
|
|
||||||
if (isEmpty(plain)) return "";
|
|
||||||
|
|
||||||
byte[] bytes = service().encrypt(plain.getBytes(), key);
|
|
||||||
return Base64.getEncoder().encodeToString(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void encrypt(Function<T, String> getter, BiConsumer<T, String> setter, Iterable<T> objs) {
|
|
||||||
convert(
|
|
||||||
obj -> {
|
|
||||||
String str = getter.apply(obj);
|
|
||||||
return encrypt(str);
|
|
||||||
},
|
|
||||||
setter,
|
|
||||||
objs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void encrypt(Function<T, String> getter, BiConsumer<T, String> setter, T... objs) {
|
|
||||||
encrypt(getter, setter, List.of(objs));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encrypt(String fieldName, Iterable<DataObject> rows) {
|
|
||||||
encrypt(
|
|
||||||
row -> row.string(fieldName),
|
|
||||||
(row, str) -> row.set(fieldName, str),
|
|
||||||
rows
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encrypt(String fieldName, DataObject... rows) {
|
|
||||||
encrypt(fieldName, List.of(rows));
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void convert(Function<T, String> converter, BiConsumer<T, String> setter, Iterable<T> objs) {
|
|
||||||
if (isEmpty(objs)) return;
|
|
||||||
|
|
||||||
objs.forEach(obj -> {
|
|
||||||
String str = converter.apply(obj);
|
|
||||||
setter.accept(obj, str);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private EgovPasswordEncoder passwordEncoder() {
|
|
||||||
if (passwordEncoder == null) {
|
|
||||||
passwordEncoder = new EgovPasswordEncoder();
|
|
||||||
String hashed = passwordEncoder.encryptPassword(notEmpty(key, "key"));
|
|
||||||
passwordEncoder.setHashedPassword(hashed);
|
|
||||||
if (!isEmpty(algorithm))
|
|
||||||
passwordEncoder.setAlgorithm(algorithm);
|
|
||||||
}
|
|
||||||
return passwordEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EgovARIACryptoServiceImpl service() {
|
|
||||||
if (aria == null) {
|
|
||||||
aria = new EgovARIACryptoServiceImpl();
|
|
||||||
aria.setPasswordEncoder(passwordEncoder());
|
|
||||||
aria.setBlockSize(getBlockSize());
|
|
||||||
}
|
|
||||||
return aria;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**주어진 문자열(암호화)을 복호화하여 반환한다.
|
|
||||||
* @param encrypted 문자열(암호화)
|
|
||||||
* @return 복호화한 문자열
|
|
||||||
*/
|
|
||||||
public String decrypt(String encrypted) {
|
|
||||||
if (isEmpty(encrypted))
|
|
||||||
return "";
|
|
||||||
|
|
||||||
byte[] bytes = Base64.getDecoder().decode(encrypted);
|
|
||||||
return new String(service().decrypt(bytes, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void decrypt(Function<T, String> getter, BiConsumer<T, String> setter, Iterable<T> objs) {
|
|
||||||
convert(
|
|
||||||
obj -> {
|
|
||||||
String str = getter.apply(obj);
|
|
||||||
return decrypt(str);
|
|
||||||
},
|
|
||||||
setter,
|
|
||||||
objs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void decrypt(Function<T, String> getter, BiConsumer<T, String> setter, T... objs) {
|
|
||||||
decrypt(getter, setter, List.of(objs));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void decrypt(String fieldName, Iterable<DataObject> rows) {
|
|
||||||
decrypt(
|
|
||||||
row -> row.string(fieldName),
|
|
||||||
(row, str) -> row.set(fieldName, str),
|
|
||||||
rows
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void decrypt(String fieldName, DataObject... rows) {
|
|
||||||
decrypt(fieldName, List.of(rows));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**현재 설정상태를 비운다.
|
|
||||||
* @return 현재 ARIA
|
|
||||||
*/
|
|
||||||
public ARIA clear() {
|
|
||||||
key = algorithm = null;
|
|
||||||
blockSize = 0;
|
|
||||||
passwordEncoder = null;
|
|
||||||
aria = null;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,172 +0,0 @@
|
|||||||
package cokr.xit.foundation.data.paging;
|
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.apache.ibatis.executor.Executor;
|
|
||||||
import org.apache.ibatis.executor.resultset.ResultSetHandler;
|
|
||||||
import org.apache.ibatis.mapping.MappedStatement;
|
|
||||||
import org.apache.ibatis.plugin.Intercepts;
|
|
||||||
import org.apache.ibatis.plugin.Signature;
|
|
||||||
import org.apache.ibatis.session.ResultHandler;
|
|
||||||
import org.apache.ibatis.session.RowBounds;
|
|
||||||
|
|
||||||
import cokr.xit.foundation.data.ARIA;
|
|
||||||
|
|
||||||
@Intercepts({
|
|
||||||
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
|
|
||||||
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}),
|
|
||||||
|
|
||||||
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
|
|
||||||
})
|
|
||||||
public class EncryptionSupport extends MybatisPlugin {
|
|
||||||
private boolean enabled;
|
|
||||||
private ARIA aria;
|
|
||||||
|
|
||||||
private List<Adaptor> adaptors = Collections.emptyList();
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEncryptor(ARIA encryptor) {
|
|
||||||
this.aria = encryptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAdaptors(Adaptor... adaptors) {
|
|
||||||
this.adaptors = List.of(adaptors);
|
|
||||||
this.adaptors.forEach(adaptor -> adaptor.setARIA(aria));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure() {
|
|
||||||
String str = properties.getProperty("enabled", "");
|
|
||||||
setEnabled(!"false".equals(str));
|
|
||||||
|
|
||||||
if (aria == null) {
|
|
||||||
aria = new ARIA();
|
|
||||||
aria.setKey(properties.getProperty("enc.key"));
|
|
||||||
str = properties.getProperty("enc.algorithm");
|
|
||||||
if (!isEmpty(str))
|
|
||||||
aria.setAlgorithm(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
str = properties.getProperty("enc.adaptor", "");
|
|
||||||
if (isEmpty(str)) return;
|
|
||||||
|
|
||||||
List<Adaptor> adaptors = Stream.of(str.split(",")).map(name -> {
|
|
||||||
try {
|
|
||||||
Class<?> klass = Class.forName(name);
|
|
||||||
return (Adaptor)klass.getConstructor().newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
}
|
|
||||||
}).toList();
|
|
||||||
setAdaptors(adaptors.toArray(new Adaptor[adaptors.size()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object query(Executor executor, MappedStatement mappedStatement, Object obj, RowBounds rowBounds, ResultHandler<?> resultHandler) throws SQLException {
|
|
||||||
encrypt(obj);
|
|
||||||
return executor.query(mappedStatement, obj, rowBounds, resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object handle(ResultSetHandler resultSetHandler, Statement statement) throws SQLException {
|
|
||||||
Object obj = super.handle(resultSetHandler, statement);
|
|
||||||
|
|
||||||
if (obj instanceof List) {
|
|
||||||
List<?> list = (List<?>)obj;
|
|
||||||
list.forEach(this::decrypt);
|
|
||||||
} else {
|
|
||||||
decrypt(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void decrypt(Object obj) {
|
|
||||||
if (!enabled || adaptors.isEmpty()) return;
|
|
||||||
|
|
||||||
adaptors.forEach(adaptor -> {
|
|
||||||
if (!adaptor.supports(obj)) return;
|
|
||||||
|
|
||||||
adaptor
|
|
||||||
.setTarget(obj)
|
|
||||||
.decrypt()
|
|
||||||
.clear();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object update(Executor executor, MappedStatement mappedStatement, Object obj) throws SQLException {
|
|
||||||
switch (mappedStatement.getSqlCommandType()) {
|
|
||||||
case INSERT:
|
|
||||||
case UPDATE: encrypt(obj); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.update(executor, mappedStatement, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encrypt(Object obj) {
|
|
||||||
if (!enabled || obj == null || adaptors.isEmpty()) return;
|
|
||||||
|
|
||||||
process(obj, arg -> adaptors.forEach(adaptor -> encrypt(adaptor, arg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encrypt(Adaptor adaptor, Object obj) {
|
|
||||||
if (!adaptor.supports(obj)) return;
|
|
||||||
|
|
||||||
adaptor
|
|
||||||
.setTarget(obj)
|
|
||||||
.encrypt()
|
|
||||||
.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class Adaptor {
|
|
||||||
protected ARIA aria;
|
|
||||||
protected Object obj;
|
|
||||||
|
|
||||||
public Adaptor setARIA(ARIA aria) {
|
|
||||||
this.aria = aria;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Adaptor setTarget(Object obj) {
|
|
||||||
this.obj = obj;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract boolean supports(Object obj);
|
|
||||||
|
|
||||||
public abstract boolean isEncrypted(String str);
|
|
||||||
|
|
||||||
public abstract Adaptor encrypt();
|
|
||||||
|
|
||||||
public abstract Adaptor decrypt();
|
|
||||||
|
|
||||||
protected void encrypt(Supplier<String> getter, Consumer<String> setter) {
|
|
||||||
String plain = getter.get();
|
|
||||||
if (isEmpty(plain) || isEncrypted(plain)) return;
|
|
||||||
|
|
||||||
setter.accept(aria.encrypt(plain));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void decrypt(Supplier<String> getter, Consumer<String> setter) {
|
|
||||||
String encrypted = getter.get();
|
|
||||||
if (isEmpty(encrypted) || !isEncrypted(encrypted)) return;
|
|
||||||
|
|
||||||
setter.accept(aria.decrypt(encrypted));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
obj = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,30 @@
|
|||||||
|
package cokr.xit.foundation.util;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Condition;
|
||||||
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
|
|
||||||
|
public class ConditionSupport {
|
||||||
|
public static class InBoot implements Condition {
|
||||||
|
private Boolean inBoot;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||||
|
if (inBoot != null)
|
||||||
|
return inBoot;
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream input = new ClassPathResource("application.yml").getInputStream();
|
||||||
|
if (input == null)
|
||||||
|
input = new ClassPathResource("application.properties").getInputStream();
|
||||||
|
|
||||||
|
return inBoot = input != null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return inBoot = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue