gpt4 book ai didi

java - 使用 Spring Data JPA 和 JPA EntityListener 进行字段级加密

转载 作者:行者123 更新时间:2023-12-01 10:38:39 25 4
gpt4 key购买 nike

我试图在插入/更新之前加密域实体上的一些字段,并在选择显示在 UI 中时解密它们。

我正在使用带有 Hibernate 和 EntityListener 的 Spring Data JPA 存储库,它在 @PostLoad 生命周期事件期间解密并在 @PrePersist 和 @PreUpdate 期间加密。我遇到的问题是,一旦将记录从 DB 加载到 PersistenceContext 中,监听器就会解密数据,这会使 EntityManager 认为实体已被更改,从而触发更新并因此再次触发 @PreUpdate 加密。关于如何处理这个问题的任何建议?

  • Spring 4.0.4.RELEASE
  • Spring Data JPA 1.5.2.RELEASE
  • hibernate 4.2.14.Final

  • 有没有一种简单的方法可以从 JPA 存储库返回分离的实体?

    实体类
    @Entity
    @Table(name="cases")
    @EntityListeners(EncryptionListener.class)
    public class MyCase implements Serializable, EncryptionEntity {

    private static final Logger logger = LoggerFactory.getLogger(MyCase.class);

    private static final long serialVersionUID = 1L;

    private String caseNumber;
    private byte[] secretProperty;
    private byte[] iv;

    @Id
    @Column(name="case_number")
    public String getCaseNumber() {
    return caseNumber;
    }
    public void setCaseNumber(String caseNumber) {
    this.caseNumber = caseNumber;
    }

    @Column(name="secret_property")
    public byte[] getSecretProperty() {
    return secretProperty;
    }
    public void setSecretProperty(byte[] secretProperty) {
    this.secretProperty = secretProperty;
    }

    @Column
    public byte[] getIv() {
    return iv;
    }
    public void setIv(byte[] iv) {
    this.iv = iv;
    }

    @Override
    @Transient
    public byte[] getInitializationVector() {
    return this.iv;
    }

    @Override
    public void setInitializationVector(byte[] iv) {
    this.setIv(iv);
    }

    }

    加密实体接口(interface)
    public interface EncryptionEntity {

    public byte[] getInitializationVector();
    public void setInitializationVector(byte[] iv);
    }

    Spring Data JPA 存储库
    public interface MyCaseRepository extends JpaRepository<MyCase, String> {

    }

    MyCaseService 接口(interface)
    public interface MyCaseService {

    public MyCase findOne(String caseNumber);

    public MyCase save(MyCase case);

    }

    MyCaseService 实现
    public class MyCaseServiceImpl implements MyCaseService {

    private static final Logger logger = LoggerFactory.getLogger(MyCaseServiceImpl.class);

    @Autowired
    private MyCaseRepository repos;


    @Override
    public MyCase findOne(String caseNumber) {
    return repos.findOne(caseNumber);
    }

    @Transactional(readOnly=false)
    public MyCase save(MyCase case) {

    return repos.save(case);
    }

    }

    加密 JPA 监听器类
    @Component
    public class EncryptionListener {

    private static final Logger logger = LoggerFactory.getLogger(EncryptionListener.class);

    private static EncryptionUtils encryptionUtils;
    private static SecureRandom secureRandom;

    private static Map<Class<? extends EncryptionEntity>,
    List<EncryptionEntityProperty>> propertiesToEncrypt;

    @Autowired
    public void setCrypto(EncryptionUtils encryptionUtils){
    EncryptionListener.encryptionUtils = encryptionUtils;
    }

    @Autowired
    public void setSecureRandom(SecureRandom secureRandom){
    EncryptionListener.secureRandom = secureRandom;
    }

    public EncryptionListener(){

    if (propertiesToEncrypt == null){

    propertiesToEncrypt = new HashMap<Class<? extends EncryptionEntity>, List<EncryptionEntityProperty>>();

    //MY CASE
    List<EncryptionEntityProperty> propertyList = new ArrayList<EncryptionEntityProperty>();
    propertyList.add(new EncryptionEntityProperty(MyCase.class, "secretProperty", byte[].class));
    propertiesToEncrypt.put(MyCase.class, propertyList);

    }

    }

    @PrePersist
    public void prePersistEncryption(EncryptionEntity entity){
    logger.debug("PRE-PERSIST");
    encryptFields(entity);
    }

    @PreUpdate
    public void preUpdateEncryption(EncryptionEntity entity){
    logger.debug("PRE-UPDATE");
    encryptFields(entity);
    }

    public void encryptFields(EncryptionEntity entity){
    byte[] iv = new byte[16];
    secureRandom.nextBytes(iv);
    encryptionUtils.setIv(iv);
    entity.setInitializationVector(iv);

    logger.debug("Encrypting " + entity);

    Class<? extends EncryptionEntity> entityClass = entity.getClass();

    List<EncryptionEntityProperty> properties = propertiesToEncrypt.get(entityClass);

    for (EncryptionEntityProperty property : properties){

    logger.debug("Encrypting '{}' field of {}", property.getName(), entityClass.getSimpleName());
    if (property.isEncryptedWithIv() == false){
    logger.debug("Encrypting '{}' without IV.", property.getName());
    }

    try {
    byte[] bytesToEncrypt = (byte[]) property.getGetter().invoke(entity, (Object[]) null);

    if (bytesToEncrypt == null || bytesToEncrypt.length == 0){
    continue;
    }

    byte[] encrypted = encryptionUtils.encrypt(bytesToEncrypt, property.isEncryptedWithIv());

    property.getSetter().invoke(entity, new Object[]{encrypted});


    } catch (Exception e){
    logger.error("Error while encrypting '{}' property of {}: " + e.getMessage(), property.getName(), entityClass.toString());
    e.printStackTrace();
    }

    }

    }

    @PostLoad
    public void decryptFields(EncryptionEntity entity){

    logger.debug("POST-LOAD");

    logger.debug("Decrypting " + entity);

    Class<? extends EncryptionEntity> entityClass = entity.getClass();
    byte[] iv = entity.getInitializationVector();

    List<EncryptionEntityProperty> properties = propertiesToEncrypt.get(entityClass);

    for (EncryptionEntityProperty property : properties){

    try {
    byte[] value = (byte[]) property.getGetter().invoke(entity, (Object[]) null);

    if (value == null || value.length == 0){
    logger.debug("Ignoring blank field {} of {}", property.getName(), entityClass.getSimpleName());
    continue;
    }

    logger.debug("Decrypting '{}' field of {}", property.getName(), entityClass.getSimpleName());
    if (property.isEncryptedWithIv() == false){
    logger.debug("Decrypting '{}' without IV.", property.getName());
    }

    byte[] decrypted = encryptionUtils.decrypt(value, iv, property.isEncryptedWithIv());

    property.getSetter().invoke(entity, new Object[]{decrypted});

    } catch (Exception e){
    logger.error("Error while decrypting '{}' property of {}", property.getName(), entityClass.toString());
    e.printStackTrace();
    }
    }

    }



    }

    最佳答案

    我知道问的问题很老了。但我遇到了同样的问题并且要克服,您可以使用 PreLoadEventListener。在您的 decryptFields而不是使用 @PostLoad使用 @PreLoad

    关于java - 使用 Spring Data JPA 和 JPA EntityListener 进行字段级加密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26305168/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com