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

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

Java hibernate conditionally apply annotation to a field?

问题

场景: 数据对象持久保存在数据库表中。表中有一些旧条目。现在我需要对表中的新条目应用加密。因此,我添加了一个新列,默认情况下该列的"encrypted"字段设置为False,以便检查这些值是否已加密。

问题: 我想在持久化数据模型(POJO)之前编写一个注解,以在持久化之前加密字段,并且只有在调用getter()时才对其进行解密(如果已加密)。

上下文:

用户模型:

  1. public class UserData {
  2. @Id
  3. @Column(name = "ID", length = 36)
  4. private String id;
  5. @Column(name = "IS_ENCRYPTED")
  6. private boolean isEncrypted;
  7. @Column(name = "NAME")
  8. @Convert(converter = EncryptionConverter.class)
  9. private String name;
  10. // 更多字段...
  11. public String getId() {
  12. return id;
  13. }
  14. public void setId(String id) {
  15. this.id = id;
  16. }
  17. // 更多类似的getter和setter方法
  18. }

我编写的加密类:

  1. @Converter
  2. public class EncryptionConverter implements AttributeConverter<String, String> {
  3. private final String secretKey = "someSecret";
  4. UserData Data = new UserData();
  5. @Override
  6. public String convertToDatabaseColumn(String str) {
  7. if (!isNullOrBlank(str))
  8. return AesEncrypt.encrypt(str, secretKey);
  9. return str;
  10. }
  11. @Override
  12. public String convertToEntityAttribute(String encryptedStr) {
  13. if (!isNullOrBlank(encryptedStr) && Data.isEncrypted)
  14. return AesEncrypt.decrypt(encryptedStr, secretKey);
  15. return encryptedStr;
  16. }
  17. }

此类位于模型类内部。(可以移到外部,但如何将"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.

  1. public class UserData {
  2. @Id
  3. @Column(name = &quot;ID&quot;, length = 36)
  4. private String id;
  5. @Column(name = &quot;IS_ENCRYPTED&quot;)
  6. private boolean isEncrypted;
  7. @Column(name = &quot;NAME&quot;)
  8. @Convert(converter = EncryptionConverter.class)
  9. private String name;
  10. // more fields ....
  11. public String getId() {
  12. return id;
  13. }
  14. public void setId(String id) {
  15. this.id = id;
  16. }
  17. // more similar getter and setters
  18. }

The encryption class that i have written.

  1. @Converter
  2. public class EncryptionConverter implements AttributeConverter&lt;String, String&gt;{
  3. private final String secretKey= &quot;someSecret&quot;;
  4. UserData Data = new UserData();
  5. @Override
  6. public String convertToDatabaseColumn(String str) {
  7. if(!isNullOrBlank(str))
  8. return AesEncrypt.encrypt(str, secretKey);
  9. return str;
  10. }
  11. @Override
  12. public String convertToEntityAttribute(String encrypedStr) {
  13. if(!isNullOrBlank(encrypedStr) &amp;&amp; Data.isEncrypted)
  14. return AesEncrypt.decrypt(encrypedStr, secretKey);
  15. return encrypedStr;
  16. }
  17. }

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

  1. @EnableAutoConfiguration
  2. public class EncryptedPropertyConfig {
  3. public EncryptedPropertyConfig() {
  4. }
  5. @Bean
  6. public EncryptablePropertyResolver encryptablePropertyResolver() {
  7. EncryptablePropertyResolver r = new MyPropertyPlaceholderConfigurer();
  8. return r;
  9. }
  10. }
  11. public final class MyPropertyPlaceholderConfigurer implements EncryptablePropertyResolver {
  12. private StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
  13. private EnvironmentStringPBEConfig envConfig = new EnvironmentStringPBEConfig();
  14. public MyPropertyPlaceholderConfigurer() {
  15. // set the encryption key and config
  16. }
  17. public String resolvePropertyValue(String passedValue) {
  18. if (!PropertyValueEncryptionUtils.isEncryptedValue(passedValue)) {
  19. return passedValue;
  20. } else {
  21. String returnValue = "";
  22. try {
  23. returnValue = PropertyValueEncryptionUtils.decrypt(passedValue, this.encryptor);
  24. return returnValue;
  25. } catch (Exception var4) {
  26. throw new RuntimeException("Error in decryption of property value:" + passedValue, var4);
  27. }
  28. }
  29. }
  30. }
英文:

You can create the encryption behaviour in another configuration class, say EncryptedPropertyConfig, in this you can create a bean, EncryptablePropertyResolver from jasypt-spring-boot

  1. @EnableAutoConfiguration
  2. public class EncryptedPropertyConfig {
  3. public EncryptedPropertyConfig() {
  4. }
  5. @Bean
  6. public EncryptablePropertyResolver encryptablePropertyResolver() {
  7. EncryptablePropertyResolver r = new MyPropertyPlaceholderConfigurer();
  8. return r;
  9. }
  10. }
  11. public final class MyPropertyPlaceholderConfigurer implements EncryptablePropertyResolver {
  12. private StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
  13. private EnvironmentStringPBEConfig envConfig = new EnvironmentStringPBEConfig();
  14. public MyPropertyPlaceholderConfigurer() {
  15. // set the encryption key and config
  16. }
  17. public String resolvePropertyValue(String passedValue) {
  18. if (!PropertyValueEncryptionUtils.isEncryptedValue(passedValue)) {
  19. return passedValue;
  20. } else {
  21. String returnValue = &quot;&quot;;
  22. try {
  23. returnValue = PropertyValueEncryptionUtils.decrypt(passedValue, this.encryptor);
  24. return returnValue;
  25. } catch (Exception var4) {
  26. throw new RuntimeException(&quot;Error in decryption of property value:&quot; + passedValue, var4);
  27. }
  28. }
  29. }
  30. }

答案2

得分: 1

我建议使用实体监听器(Entity Listeners)来提供另一种解决方案。

  1. import javax.persistence.PostLoad;
  2. import javax.persistence.PreUpdate;
  3. public class UserData {
  4. private final String secretKey = "someSecret";
  5. // ...
  6. @PreUpdate
  7. private void onUpdate() {
  8. // 在将实体保存到数据库之前触发(包括创建和更新)
  9. if (!isNullOrBlank(name)) {
  10. name = AesEncrypt.encrypt(name, secretKey);
  11. }
  12. }
  13. @PostLoad
  14. private void onLoad() {
  15. // 在从实体提供程序获取实体后触发
  16. if (!isNullOrBlank(name) && isEncrypted) {
  17. name = AesEncrypt.decrypt(name, secretKey);
  18. }
  19. }
  20. }
英文:

I suggest alternative solution using Entity Listeners

  1. import javax.persistence.PostLoad;
  2. import javax.persistence.PreUpdate;
  3. public class UserData {
  4. private final String secretKey= &quot;someSecret&quot;;
  5. // ...
  6. @PreUpdate
  7. private void onUpdate() {
  8. // triggered before saving entity to DB (both create &amp; update)
  9. if(!isNullOrBlank(name)) {
  10. name = AesEncrypt.encrypt(name, secretKey);
  11. }
  12. }
  13. @PostLoad
  14. private void onLoad() {
  15. // triggered after entity is fetched from Entity Provider
  16. if (!isNullOrBlank(name) &amp;&amp; isEncrypted) {
  17. name = AesEncrypt.decrypt(name, secretKey);
  18. }
  19. }
  20. }

答案3

得分: 1

以下是您要翻译的代码部分:

  1. 而不是使用JPA AttributeConverter您可以以这种方式实现Hibernate [用户类型](https://docs.jboss.org/hibernate/stable/orm/userguide/html_single/Hibernate_User_Guide.html#basic-custom-type-UserType):
  2. ``` java
  3. import java.util.Objects;
  4. import org.hibernate.HibernateException;
  5. import org.hibernate.engine.spi.SharedSessionContractImplementor;
  6. import org.hibernate.type.StringType;
  7. import org.hibernate.usertype.UserType;
  8. public class CustomNameType implements UserType
  9. {
  10. private String secretKey = "someSecret";
  11. public CustomNameType()
  12. {
  13. }
  14. @Override
  15. public Object deepCopy(Object value) throws HibernateException
  16. {
  17. if (null == value) return null;
  18. return ((CustomName) value).clone();
  19. }
  20. @Override
  21. public Object assemble(Serializable cached, Object owner) throws HibernateException
  22. {
  23. return cached;
  24. }
  25. @Override
  26. public Serializable disassemble(Object value) throws HibernateException
  27. {
  28. return (Serializable) value;
  29. }
  30. @Override
  31. public Object replace(Object original, Object target, Object owner) throws HibernateException
  32. {
  33. return original;
  34. }
  35. @Override
  36. public boolean equals(Object one, Object two) throws HibernateException
  37. {
  38. return Objects.equals(one, two);
  39. }
  40. @Override
  41. public int hashCode(Object obj) throws HibernateException
  42. {
  43. return Objects.hashCode(obj);
  44. }
  45. @Override
  46. public boolean isMutable()
  47. {
  48. return true;
  49. }
  50. @Override
  51. public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
  52. throws HibernateException, SQLException
  53. {
  54. boolean isEncrypted = rs.getBoolean(0); // IS_ENCRYPTED
  55. String name = rs.getString(1); // NAME
  56. if (isEncrypted) {
  57. name = AesEncrypt.decrypt(name, secretKey);
  58. }
  59. return new CustomName(isEncrypted, name);
  60. }
  61. @Override
  62. public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session)
  63. throws HibernateException, SQLException
  64. {
  65. CustomName customName = (CustomName) value;
  66. String name = customName.getName();
  67. if (customName.isEncrypted()) {
  68. name = AesEncrypt.encrypt(name, secretKey);
  69. }
  70. statement.setBoolean(0, customName.isEncrypted());
  71. statement.setString(1, name);
  72. }
  73. @Override
  74. public Class<?> returnedClass()
  75. {
  76. return CustomName.class;
  77. }
  78. @Override
  79. public int[] sqlTypes()
  80. {
  81. // 我不知道您的IS_ENCRYPTED和NAME字段的类型
  82. // 因此,这个地方可能需要更正
  83. int[] types = {BooleanType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType()};
  84. return types;
  85. }
  86. }

其中CustomName是:

  1. public class CustomName implements Serializable, Cloneable
  2. {
  3. private boolean isEncrypted;
  4. private String name;
  5. public CustomName(boolean isEncrypted, String name)
  6. {
  7. this.isEncrypted = isEncrypted;
  8. this.name = name;
  9. }
  10. // 获取器,equals,hashCode ...
  11. @Override
  12. public CustomName clone()
  13. {
  14. return new CustomName(isEncrypted, name);
  15. }
  16. }

然后使用它:

  1. import org.hibernate.annotations.Type;
  2. import org.hibernate.annotations.Columns;
  3. @Entity
  4. public class UserData {
  5. @Type(type = "com.your.CustomNameType")
  6. @Columns(columns = {
  7. @Column(name = "IS_ENCRYPTED"),
  8. @Column(name = "NAME")
  9. })
  10. private CustomName name;
  11. }
英文:

Instead of using JPA AttributeConverter you can implement hibernate user type in this way:

  1. import java.util.Objects;
  2. import org.hibernate.HibernateException;
  3. import org.hibernate.engine.spi.SharedSessionContractImplementor;
  4. import org.hibernate.type.StringType;
  5. import org.hibernate.usertype.UserType;
  6. public class CustomNameType implements UserType
  7. {
  8. private String secretKey = &quot;someSecret&quot;;
  9. public CustomNameType()
  10. {
  11. }
  12. @Override
  13. public Object deepCopy(Object value) throws HibernateException
  14. {
  15. if (null == value) return null;
  16. return ((CustomName) value).clone();
  17. }
  18. @Override
  19. public Object assemble(Serializable cached, Object owner) throws HibernateException
  20. {
  21. return cached;
  22. }
  23. @Override
  24. public Serializable disassemble(Object value) throws HibernateException
  25. {
  26. return (Serializable) value;
  27. }
  28. @Override
  29. public Object replace(Object original, Object target, Object owner) throws HibernateException
  30. {
  31. return original;
  32. }
  33. @Override
  34. public boolean equals(Object one, Object two) throws HibernateException
  35. {
  36. return Objects.equals(one, two);
  37. }
  38. @Override
  39. public int hashCode(Object obj) throws HibernateException
  40. {
  41. return Objects.hashCode(obj);
  42. }
  43. @Override
  44. public boolean isMutable()
  45. {
  46. return true;
  47. }
  48. @Override
  49. public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
  50. throws HibernateException, SQLException
  51. {
  52. boolean isEncrypted = rs.getBoolean(0); // IS_ENCRYPTED
  53. String name = rs.getString(1); // NAME
  54. if (isEncrypted) {
  55. name = AesEncrypt.decrypt(name, secretKey);
  56. }
  57. return new CustomName(isEncrypted, name);
  58. }
  59. @Override
  60. public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session)
  61. throws HibernateException, SQLException
  62. {
  63. CustomName customName = (CustomName) value;
  64. String name = customName.getName();
  65. if (customName.isEncrypted()) {
  66. name = AesEncrypt.encrypt(name, secretKey);
  67. }
  68. statement.setBoolean(0, customName.isEncrypted());
  69. statement.setString(1, name);
  70. }
  71. @Override
  72. public Class&lt;?&gt; returnedClass()
  73. {
  74. return CustomName.class;
  75. }
  76. @Override
  77. public int[] sqlTypes()
  78. {
  79. // I do not know the types of your IS_ENCRYPTED and NAME fields
  80. // So, this place maybe require correction
  81. int[] types = {BooleanType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType()};
  82. return types;
  83. }
  84. }

where CustomName is:

  1. public class CustomName implements Serializable, Cloneable
  2. {
  3. private boolean isEncrypted;
  4. private String name;
  5. public CustomName(boolean isEncrypted, String name)
  6. {
  7. this.isEncrypted = isEncrypted;
  8. this.name = name;
  9. }
  10. // getters , equals, hashCode ...
  11. @Override
  12. public CustomName clone()
  13. {
  14. return new CustomName(isEncrypted, name);
  15. }
  16. }

and then use it:

  1. import org.hibernate.annotations.Type;
  2. import org.hibernate.annotations.Columns;
  3. @Entity
  4. public class UserData {
  5. @Type(type = &quot;com.your.CustomNameType&quot;)
  6. @Columns(columns = {
  7. @Column(name = &quot;IS_ENCRYPTED&quot;),
  8. @Column(name = &quot;NAME&quot;)
  9. })
  10. private CustomName name;
  11. }

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:

确定