尝试读取加密文本时 JPA/Hibernate 出现 OptimisticLockException
我们需要在使用 JPA 存储字符串时对其进行加密,并在读取字符串时对其进行解密。首先,我们不能使用某些 hibernate 配置或任何其他配置文件,因为我们的属性是通用的。这意味着我们的属性看起来像这样(简单来说,我们已经在这里实现了一些处理):(
@Entity
@Table(name = "PROPERTY")
@Access(AccessType.FIELD)
public class Property implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "PRO_ID")
private long id;
@Version
@Temporal(value = TemporalType.TIMESTAMP)
@Column(name = "PRO_VERSION")
private Date version;
@Column(name = "PRO_KEY")
private String key;
@Transient
private String value;
@Column(name = "PRO_ENCRYPTED")
private boolean encrypted = false;
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public Date getVersion()
{
return version;
}
public void setVersion(Date version)
{
this.version = version;
}
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
public boolean isEncrypted()
{
return encrypted;
}
public void setEncrypted(boolean encrypted)
{
this.encrypted = encrypted;
}
@Access(AccessType.PROPERTY)
@Column(name = "PRO_VALUE")
protected String getValueDatabase()
{
// TODO: add decryption
return value;
}
protected void setValueDatabase(String value)
{
// TODO: add encryption
this.value = value;
}
}
我们可以使用 isEncrypted() 方法区分哪些属性必须加密。根据属性设置为 true 或 false)。 Out Encryptor 类(简化)与 Jasypt 配合使用:
public final class MyEncrypter
{
private static final String password = "AHKG@a4SjHH5%j%974";
private static final StandardPBEStringEncryptor encryptor;
static
{
encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword(password);
}
public final static String encrypt(String string)
{
return encryptor.encrypt(string);
}
public final static String decrypt(String encrypted)
{
return encryptor.decrypt(encrypted);
}
}
加密/解密有效。实际上我们的整个持久化处理都是有效的。我们可以保存加密的数据。但是,当我们读取加密数据时,我们得到以下结果:
javax.persistence.OptimisticLockException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [at.jit.remind.jee.domain.context.model.Property#10]
奇怪的是,只有当且仅当 Hibernates 尝试更新加密文本时,才会发生这种情况。但当我们对文本进行伪加密和伪解密时,这种情况就不会发生。这意味着我们反转文本,使用 Hibernate 存储反转的文本,并在 Hibernate 尝试更新它时“解密”它。所以我们的整个处理是有效的,但是当我们使用“真正的”加密时它不起作用的原因可能是什么?有什么想法吗?
(同样,加密和解密本身可以工作。我让它在不同的 JUnit 类中运行以确保这一点。)
We need to encrypt Strings while storing them with JPA and decrypt it when reading it. First of all, we can't use some hibernate config or any other config file because our properties are generic. That means that our properties look like this (simplyfied, we already implemented some handling here):
@Entity
@Table(name = "PROPERTY")
@Access(AccessType.FIELD)
public class Property implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "PRO_ID")
private long id;
@Version
@Temporal(value = TemporalType.TIMESTAMP)
@Column(name = "PRO_VERSION")
private Date version;
@Column(name = "PRO_KEY")
private String key;
@Transient
private String value;
@Column(name = "PRO_ENCRYPTED")
private boolean encrypted = false;
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public Date getVersion()
{
return version;
}
public void setVersion(Date version)
{
this.version = version;
}
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
public boolean isEncrypted()
{
return encrypted;
}
public void setEncrypted(boolean encrypted)
{
this.encrypted = encrypted;
}
@Access(AccessType.PROPERTY)
@Column(name = "PRO_VALUE")
protected String getValueDatabase()
{
// TODO: add decryption
return value;
}
protected void setValueDatabase(String value)
{
// TODO: add encryption
this.value = value;
}
}
(We can distinguish what property has to be encrypted with the isEncrypted() method. This is set to true or false depending on the property). Out Encryptor class (simplified) works with Jasypt:
public final class MyEncrypter
{
private static final String password = "AHKG@a4SjHH5%j%974";
private static final StandardPBEStringEncryptor encryptor;
static
{
encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword(password);
}
public final static String encrypt(String string)
{
return encryptor.encrypt(string);
}
public final static String decrypt(String encrypted)
{
return encryptor.decrypt(encrypted);
}
}
The encryption/decryption works. Actually our whole persistence handling works. We can save encrypted data. BUT, when we read the encrypted data, we get the following:
javax.persistence.OptimisticLockException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [at.jit.remind.jee.domain.context.model.Property#10]
The strange thing is, this only happens if and only if Hibernates tries to update the encrypted text. But it DOESN'T happen, when we pseudo encrypt and pseudo decrypt our text. That means we reverse the text, store the reversed text with Hibernate and "decrypted" it when Hibernate tries to update it. So our whole handling works, but what could be the reasons that it doesn't work when we use "real" encryption? Any ideas?
(Again, the en- and decryption itself works. I let run it in different JUnit classes to ensure this.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我们决定使用另一种加密,因为显然 jasypt 对 Hibernate 做了一些奇怪的事情。似乎这种加密/解密在某种程度上为 Hibernate 产生了一个“脏标志”,因此 Hibernate 尝试更新数据库条目,尽管加密同时更改了值。不可能将其隔离。
We decided to use another encryption because obviously jasypt does something odd to Hibernate. It seems that this encryption/decryption produces somehow a "dirty flag" for Hibernate, so Hibernate tries to update the database entries although the encrytion changes values at the same time. It was not possible to isolate it.
这是因为您的对象仍然附加到休眠会话并且其内部状态发生了更改。尝试首先调用 session.evict(object),然后更改要加密的文本。
This is because your object is still attached to the hibernate session and its internal state is changed. Try first calling session.evict(object) and then change the text that is to be encrypted.