我正在为一个项目开发一些RESTFull Web服务.我使用Spring框架并使用gradle进行构建.问题是,我想在写入和读取数据时加密和解密数据表.我已经有了一个用AES等加密和解密数据的算法(类).我需要的是,如何注释这个方法来休眠实体类,我需要为这个类创建bean吗?
例如: -
@Column(columnDefinition= "LONGBLOB", name = "card_no") @ColumnTransformer( read="decrypt(card_no)", write="encrypt(?)") private String cardNo;
像这样我想在这里添加我自己的加密/解密java方法.
如果您可以访问JPA 2.1,我会主张使用@Convert
带有AttributeConverter
实现的注释.
一个AttributeConverter
限定了一个实体属性的状态之间的合同,当它是序列化到数据存储,并且当它被从数据存储反序列化.
public class CreditCard { @Convert(converter = CreditCardNumberConverter.class) private String creditCardNumber; }
您的转换器实现可能如下所示
public class CreditCardNumberConverter implements AttributeConverter{ @Override public String convertToDatabaseColumn(String attribute) { /* perform encryption here */ } @Override public String convertToEntityAttribute(String dbData) { /* perform decryption here */ } }
如果你不能够利用JPA 2.1,一个EntityListener
或使用@PrePersist
,@PreUpdate
以及@PostLoad
可以用于加密和解密该数据库值执行类似的逻辑来使用.
请确保如果您决定使用一个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); } }
执行上述操作可确保对象中的实体状态与数据库中存在的内容保持一致,而不会让Hibernate相信实体在加载数据库数据后立即更改.
你可以用几种方式做到这一点.
使用JPA Listeners
以下是一个简单的例子.请相应更改.
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()); } } }
使用此方法,您可能必须采取一些预防措施以避免加密已加密的cardNo
值.Transient
无论是否cardNo
已经加密,都可以使用附加属性来保存状态.
或者只是在entity属性的getter和setter中实现此功能.
public String getCardNo(){ return EncrypUtil.decrypt(this.cardNo); } public void setCardNo(String cardNo){ this.cardNo = EncrypUtil.encrypt(cardNo); }
您还可以使用JPA供应商特定的拦截器.即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())); }
您还可以使用@Convert
注释并指定转换器
@Convert(converter = CCConverter.class) private String creditCardNumber;
CCConverter
类应该是一个实现 AttributeConverter
希望这可以帮助.