java 如何在spring项目中使用自定义注释(休眠)加密/解密数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34259457/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 22:39:34  来源:igfitidea点击:

How to encrypt/decypt data with custom anotation(hibernate) in spring project

javahibernatejpaencryptioninterceptor

提问by samith kumarasingha

I'm developing some RESTFull web services for a project. I use the Spring framework and use gradle for build. The problem is , I want to encrypt and decrypt data table when write and read data. I already have a algorithm(class) to encrypt and decrypt data with AES etc. What I need is, how annotate this method to hibernate entity class, am I need to create bean for this class ?

我正在为一个项目开发一些 RESTFull Web 服务。我使用 Spring 框架并使用 gradle 进行构建。问题是,我想在写入和读取数据时加密和解密数据表。我已经有一个算法(类)来使用 AES 等加密和解密数据。我需要的是,如何将此方法注释为休眠实体类,我是否需要为此类创建 bean?

Ex:-

前任:-

@Column(columnDefinition= "LONGBLOB", name = "card_no")
        @ColumnTransformer(
                read="decrypt(card_no)",
                write="encrypt(?)")
        private String cardNo;

Like this I want to add my own encryption/decryption java method to here.

像这样我想在这里添加我自己的加密/解密java方法。

回答by Naros

If you have access to JPA 2.1, I would advocate for the use of an @Convertannotation with an AttributeConverterimplementation.

如果您可以访问 JPA 2.1,我会提倡在实现中使用@Convert注释AttributeConverter

An AttributeConverterdefines a contract between the state of an entity property when it is serialized to the datastore and when it is deserialized from the datastore.

AnAttributeConverter定义了实体属性在序列化到数据存储和从数据存储反序列化时的状态之间的契约。

public class CreditCard {
  @Convert(converter = CreditCardNumberConverter.class)
  private String creditCardNumber;
}

Your converter implementation might look like this

您的转换器实现可能如下所示

public class CreditCardNumberConverter implements AttributeConverter<String, String> {
  @Override
  public String convertToDatabaseColumn(String attribute) {
    /* perform encryption here */
  }
  @Override
  public String convertToEntityAttribute(String dbData) {
    /* perform decryption here */
  }
}

If you are not able to leverage JPA 2.1, an EntityListeneror the use of @PrePersist, @PreUpdate, and @PostLoadmay be used in order to perform similar logic for encrypting and decrypting the database value.

如果您无法利用 JPA 2.1,则可以使用、、 和的EntityListener或来执行类似的逻辑来加密和解密数据库值。@PrePersist@PreUpdate@PostLoad

Just be sure that if you decide to use an EntityListeneror any of the Pre/Postcallback method annotations, store the decrypted result in a transient field and use that field as your business layer's usage, such as follows:

请确保如果您决定使用一个EntityListener或任何Pre/Post回调方法注释,请将解密的结果存储在一个瞬态字段中,并将该字段用作您的业务层的用法,例如:

public class CreditCard {    

  // this field could have package private get/set methods  
  @Column(name = "card_number", length = 25, nullable = false)
  private String encrpytedCardNumber;

  // this is the public operated upon field
  @Transient
  private String cardNumber;

  @PostLoad
  public void decryptCardNumber() {
    // decrypts card number during DATABASE READ
    this.cardNumber = EncryptionUtils.decrypt(encryptedCardNumber);
  }

  @PrePersist
  @PreUpdate
  public void encryptCardNumber() {
    // encrypts card number during INSERT/UPDATE
    this.encryptedCardNumber = EncryptionUtils.encrypt(cardNumber);
  }
}

Doing the above keeps entity state consistent in the object as to what exists in your database, without having Hibernate believing that the entity has changed immediately upon loading the database data.

执行上述操作可以使对象中的实体状态与数据库中存在的内容保持一致,而无需 Hibernate 相信该实体在加载数据库数据后立即发生了更改。

回答by tharindu_DG

You could do this in several ways.

您可以通过多种方式做到这一点。

  • Using JPA Listeners
  • 使用JPA 侦听器

Following is a simple example. Please change accordingly.

下面是一个简单的例子。请相应更改。

public class CustomListener{
   @Inject
   private EncryptorBean encryptor;


   @PostLoad
   @PostUpdate
   public void decrypt(Object pc) {
      if (!(pc instanceof)) {
         return;
      }

      MyObj obj = (MyObj) pc;

      if (obj.getCardNo() != null) {
         obj.setCardNo(
            encryptor.decryptString(user.getEncryptedCardNo);
      }
   }


   @PrePersist
   @PreUpdate
   public void encrypt(Object pc) {
      if (!(pc instanceof MyObj)) {
         return;
      }

      MyObj obj = (MyObj ) pc;

      if (obj.getCardNo() != null) {
         user.setEncryptedCardNo(
            encryptor.encryptString(user.getCardNo());
      }
   }
}

With this approach, you might have to take some precaution to avoid encrypting a already encrypted cardNovalue. An additional Transientproperty could be used to hold the state whether the cardNois already encrypted or not.

使用这种方法,您可能需要采取一些预防措施来避免加密已经加密的cardNo值。附加Transient属性可用于保存状态是否cardNo已加密。

  • Or Simply Implementing this feature in the getters and setters of the entity property.

     public String getCardNo(){
         return EncrypUtil.decrypt(this.cardNo);
     }
    
     public void setCardNo(String cardNo){
         this.cardNo = EncrypUtil.encrypt(cardNo);
     }
    
  • You could also use JPA vendor specific interceptors. i.e HibernateInterceptors

    public class CustomInterceptor extends EmptyInterceptor{
    
        public boolean onSave(Object entity,Serializable id,
             Object[] state,String[] propertyNames,Type[] types)
             throws CallbackException {
    
             if (entity instanceof MyObj){
                 // check if already encrypted or not.
                 //(A transient property could be useful)
                 entity.setCardNo(EncrypUtils.encrypt(entity.getCardNo()));
             }
    
  • You could also use @Convertannotation and specify a converter

        @Convert(converter = CCConverter.class)
        private String creditCardNumber;
    

    CCConverterclass should be an implementation of AttributeConverter

  • 或者简单地在实体属性的 getter 和 setter 中实现此功能。

     public String getCardNo(){
         return EncrypUtil.decrypt(this.cardNo);
     }
    
     public void setCardNo(String cardNo){
         this.cardNo = EncrypUtil.encrypt(cardNo);
     }
    
  • 您还可以使用 JPA 供应商特定的拦截器。IEHibernateInterceptors

    public class CustomInterceptor extends EmptyInterceptor{
    
        public boolean onSave(Object entity,Serializable id,
             Object[] state,String[] propertyNames,Type[] types)
             throws CallbackException {
    
             if (entity instanceof MyObj){
                 // check if already encrypted or not.
                 //(A transient property could be useful)
                 entity.setCardNo(EncrypUtils.encrypt(entity.getCardNo()));
             }
    
  • 您还可以使用@Convert注释并指定转换器

        @Convert(converter = CCConverter.class)
        private String creditCardNumber;
    

    CCConverter类应该是一个实现 AttributeConverter

Hope this helps.

希望这可以帮助。