Java-Ruhezustand bedingt Annotation auf ein Feld anwenden?
Szenario: Ein Datenobjekt, das in der DB-Tabelle verbleibt. Es gibt einige alte Einträge in der Tabelle. Jetzt muss ich neue weitere Einträge in der Tabelle verschlüsseln. Daher füge ich eine neue Spalte hinzu, in der das Feld verschlüsselt standardmäßig auf Falsch gesetzt ist, um zu überprüfen, ob die Werte verschlüsselt sind.
Problem: Ich möchte eine Anmerkung schreiben, um die Felder im Datenmodell (POJO) zu verschlüsseln, bevor sie bei getter () -Aufrufen beibehalten und entschlüsselt werden, wenn sie verschlüsselt sind.
Kontext:
Das Benutzermodell.
public class UserData {
@Id
@Column(name = "ID", length = 36)
private String id;
@Column(name = "IS_ENCRYPTED")
private boolean isEncrypted;
@Column(name = "NAME")
@Convert(converter = EncryptionConverter.class)
private String name;
// more fields ....
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// more similar getter and setters
}
Die Verschlüsselungsklasse, die ich geschrieben habe.
@Converter
public class EncryptionConverter implements AttributeConverter<String, String>{
private final String secretKey= "someSecret";
UserData Data = new UserData();
@Override
public String convertToDatabaseColumn(String str) {
if(!isNullOrBlank(str))
return AesEncrypt.encrypt(str, secretKey);
return str;
}
@Override
public String convertToEntityAttribute(String encrypedStr) {
if(!isNullOrBlank(encrypedStr) && Data.isEncrypted)
return AesEncrypt.decrypt(encrypedStr, secretKey);
return encrypedStr;
}
}
Diese Klasse befindet sich innerhalb der Modellklasse. (Kann sich nach draußen bewegen, aber wie wird das verschlüsselte Flag an die Annotation übergeben?)
Wie kann ich das machen, ist mein Ansatz korrekt?
Bearbeiten: Es gibt mehrere Felder, die verschlüsselt / entschlüsselt werden sollen, nicht nur den Namen.
Antworten
Sie können das Verschlüsselungsverhalten in einer anderen Konfigurationsklasse erstellen, z. B. EncryptedPropertyConfig. In dieser können Sie eine Bean, EncryptablePropertyResolver, aus jasypt-spring-boot erstellen
@EnableAutoConfiguration
public class EncryptedPropertyConfig {
public EncryptedPropertyConfig() {
}
@Bean
public EncryptablePropertyResolver encryptablePropertyResolver() {
EncryptablePropertyResolver r = new MyPropertyPlaceholderConfigurer();
return r;
}
}
public final class MyPropertyPlaceholderConfigurer implements EncryptablePropertyResolver {
private StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
private EnvironmentStringPBEConfig envConfig = new EnvironmentStringPBEConfig();
public MyPropertyPlaceholderConfigurer() {
// set the encryption key and config
}
public String resolvePropertyValue(String passedValue) {
if (!PropertyValueEncryptionUtils.isEncryptedValue(passedValue)) {
return passedValue;
} else {
String returnValue = "";
try {
returnValue = PropertyValueEncryptionUtils.decrypt(passedValue, this.encryptor);
return returnValue;
} catch (Exception var4) {
throw new RuntimeException("Error in decryption of property value:" + passedValue, var4);
}
}
}
}
Ich schlage eine alternative Lösung mit Entity Listeners vor
import javax.persistence.PostLoad;
import javax.persistence.PreUpdate;
public class UserData {
private final String secretKey= "someSecret";
// ...
@PreUpdate
private void onUpdate() {
// triggered before saving entity to DB (both create & update)
if(!isNullOrBlank(name)) {
name = AesEncrypt.encrypt(name, secretKey);
}
}
@PostLoad
private void onLoad() {
// triggered after entity is fetched from Entity Provider
if (!isNullOrBlank(name) && isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
}
}
Anstatt JPA AttributeConverter zu verwenden, können Sie den Benutzertyp im Ruhezustand folgendermaßen implementieren :
import java.util.Objects;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;
public class CustomNameType implements UserType
{
private String secretKey = "someSecret";
public CustomNameType()
{
}
@Override
public Object deepCopy(Object value) throws HibernateException
{
if (null == value) return null;
return ((CustomName) value).clone();
}
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return cached;
}
@Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) value;
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
@Override
public boolean equals(Object one, Object two) throws HibernateException
{
return Objects.equals(one, two);
}
@Override
public int hashCode(Object obj) throws HibernateException
{
return Objects.hashCode(obj);
}
@Override
public boolean isMutable()
{
return true;
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException
{
boolean isEncrypted = rs.getBoolean(0); // IS_ENCRYPTED
String name = rs.getString(1); // NAME
if (isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
return new CustomName(isEncrypted, name);
}
@Override
public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException
{
CustomName customName = (CustomName) value;
String name = customName.getName();
if (customName.isEncrypted()) {
name = AesEncrypt.encrypt(name, secretKey);
}
statement.setBoolean(0, customName.isEncrypted());
statement.setString(1, name);
}
@Override
public Class<?> returnedClass()
{
return CustomName.class;
}
@Override
public int[] sqlTypes()
{
// I do not know the types of your IS_ENCRYPTED and NAME fields
// So, this place maybe require correction
int[] types = {BooleanType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType()};
return types;
}
}
wo CustomNameist:
public class CustomName implements Serializable, Cloneable
{
private boolean isEncrypted;
private String name;
public CustomName(boolean isEncrypted, String name)
{
this.isEncrypted = isEncrypted;
this.name = name;
}
// getters , equals, hashCode ...
@Override
public CustomName clone()
{
return new CustomName(isEncrypted, name);
}
}
und dann benutze es:
import org.hibernate.annotations.Type;
import org.hibernate.annotations.Columns;
@Entity
public class UserData {
@Type(type = "com.your.CustomNameType")
@Columns(columns = {
@Column(name = "IS_ENCRYPTED"),
@Column(name = "NAME")
})
private CustomName name;
}