Java中的条件性应用注解到一个字段怎么办?

huangapple go评论100阅读模式
英文:

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 = &quot;ID&quot;, length = 36)
	private String id;
	
	@Column(name = &quot;IS_ENCRYPTED&quot;)
	private boolean isEncrypted;

	@Column(name = &quot;NAME&quot;)
	@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&lt;String, String&gt;{
		private final String  secretKey= &quot;someSecret&quot;;
		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) &amp;&amp; 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 = &quot;&quot;;

            try {
                returnValue = PropertyValueEncryptionUtils.decrypt(passedValue, this.encryptor);
                return returnValue;
            } catch (Exception var4) {
                throw new RuntimeException(&quot;Error in decryption of property value:&quot; + 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= &quot;someSecret&quot;;

    // ... 
    
    @PreUpdate
    private void onUpdate() {
        // triggered before saving entity to DB (both create &amp; update)
        if(!isNullOrBlank(name)) {
            name = AesEncrypt.encrypt(name, secretKey);
        }
    }

    @PostLoad
    private void onLoad() {
        // triggered after entity is fetched from Entity Provider
        if (!isNullOrBlank(name) &amp;&amp; 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 = &quot;someSecret&quot;;

   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&lt;?&gt; 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 = &quot;com.your.CustomNameType&quot;)
   @Columns(columns = {
      @Column(name = &quot;IS_ENCRYPTED&quot;),
      @Column(name = &quot;NAME&quot;)
   })
   private CustomName name;
}

huangapple
  • 本文由 发表于 2020年8月31日 17:28:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/63668209.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定