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