英文:
Java hibernate conditionally apply annotation to a field?
问题
场景: 数据对象持久保存在数据库表中。表中有一些旧条目。现在我需要对表中的新条目应用加密。因此,我添加了一个新列,默认情况下该列的"encrypted"字段设置为False,以便检查这些值是否已加密。
问题: 我想在持久化数据模型(POJO)之前编写一个注解,以在持久化之前加密字段,并且只有在调用getter()时才对其进行解密(如果已加密)。
上下文:
用户模型:
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;
// 更多字段...
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// 更多类似的getter和setter方法
}
我编写的加密类:
@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 encryptedStr) {
if (!isNullOrBlank(encryptedStr) && Data.isEncrypted)
return AesEncrypt.decrypt(encryptedStr, secretKey);
return encryptedStr;
}
}
此类位于模型类内部。(可以移到外部,但如何将"isEncrypted"标志传递给注解?)
我应该如何做到这一点?我的方法正确吗?
注意:不仅名称需要加密/解密,还有其他多个字段。
英文:
Scenario: A data object which persists in the DB table. There are some old entries in the table. Now I have to apply encryption to new further entries in the table. So I add a new column which has the field encrypted set to False by default to check if the values are encrypted.
Problem: I want to write an annotation to encrypt the fields in the data model(POJO) before persisting and decrypt on getter() calls only if it is encrypted.
Context:
The user model.
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
}
The encryption class that i have written.
@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;
}
}
This class is inside the model class. (can move outside, but how to pass isencrypted flag to annotation)
How can I do this, is my approach correct?
Edit: there are multiple fields which are to be encrypted/decrypted not just name.
答案1
得分: 1
@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);
}
}
}
}
英文:
You can create the encryption behaviour in another configuration class, say EncryptedPropertyConfig, in this you can create a bean, EncryptablePropertyResolver from jasypt-spring-boot
@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);
}
}
}
}
答案2
得分: 1
我建议使用实体监听器(Entity Listeners)来提供另一种解决方案。
import javax.persistence.PostLoad;
import javax.persistence.PreUpdate;
public class UserData {
private final String secretKey = "someSecret";
// ...
@PreUpdate
private void onUpdate() {
// 在将实体保存到数据库之前触发(包括创建和更新)
if (!isNullOrBlank(name)) {
name = AesEncrypt.encrypt(name, secretKey);
}
}
@PostLoad
private void onLoad() {
// 在从实体提供程序获取实体后触发
if (!isNullOrBlank(name) && isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
}
}
英文:
I suggest alternative solution using Entity Listeners
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);
}
}
}
答案3
得分: 1
以下是您要翻译的代码部分:
而不是使用JPA AttributeConverter,您可以以这种方式实现Hibernate [用户类型](https://docs.jboss.org/hibernate/stable/orm/userguide/html_single/Hibernate_User_Guide.html#basic-custom-type-UserType):
``` java
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()
{
// 我不知道您的IS_ENCRYPTED和NAME字段的类型
// 因此,这个地方可能需要更正
int[] types = {BooleanType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType()};
return types;
}
}
其中CustomName
是:
public class CustomName implements Serializable, Cloneable
{
private boolean isEncrypted;
private String name;
public CustomName(boolean isEncrypted, String name)
{
this.isEncrypted = isEncrypted;
this.name = name;
}
// 获取器,equals,hashCode ...
@Override
public CustomName clone()
{
return new CustomName(isEncrypted, name);
}
}
然后使用它:
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;
}
英文:
Instead of using JPA AttributeConverter you can implement hibernate user type in this way:
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;
}
}
where CustomName
is:
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);
}
}
and then use it:
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;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论