结合使用Hibernate持久性和验证的正确方法

结合使用Hibernate持久性和验证的正确方法,hibernate,jakarta-ee,hibernate-validator,Hibernate,Jakarta Ee,Hibernate Validator,我在使用hibernate持久性和hibernate验证时遇到过这样的情况,尽管我想到了一些黑客方法,但我不知道使用hibernate持久性和验证解决这个问题的最佳方法 简单地说,我有一个用户对象,我想持久化它。但在我坚持之前,我想验证一下。相当标准的东西 现在,用户对象有密码,我们有关于有效密码的规则。例:最少8个字符,包括至少1个数字,等等…我想验证这些东西 但是当我坚持的时候,我需要加密/加密/散列密码。但是在我做了satting/hash之后,显然没有合理的方法对密码进行上述验证 所以,

我在使用hibernate持久性和hibernate验证时遇到过这样的情况,尽管我想到了一些黑客方法,但我不知道使用hibernate持久性和验证解决这个问题的最佳方法

简单地说,我有一个用户对象,我想持久化它。但在我坚持之前,我想验证一下。相当标准的东西

现在,用户对象有密码,我们有关于有效密码的规则。例:最少8个字符,包括至少1个数字,等等…我想验证这些东西

但是当我坚持的时候,我需要加密/加密/散列密码。但是在我做了satting/hash之后,显然没有合理的方法对密码进行上述验证

所以,我想我可以使用@PrePersist和@PreUpdate注释来实现这一点。我的想法是在用户类中,我有一个名为onCreate()的方法。我用@PrePersist标记了它,我做了类似的事情(我对onUpdate()有类似的东西):

我认为当我调用entityManager.persist()时,它将首先调用验证,然后调用onCreate(),然后再调用persist。因此,验证将验证原始的非盐/哈希密码。而盐析/散列将在稍后发生

不过,当我运行测试并调试时,我发现在运行验证之前会调用标记为@PrePersist的方法,这意味着我无法再验证我的密码

如何将密码的salt/hash正确地挂接到entityManager.persist()生命周期中,以便在salt/hash之后正确地进行验证,并最终持久化


谢谢。

使用两个bean属性,一个用于持久性,另一个用于验证/转换。大概是这样的:

@Entity
@MyCustomConstraint(...)
public class User implements Serializable {

    // place persistence annotations here, for example
    @Lob @Column(...)
    private byte[] hashedPassword;
    // place validation constraints here, for example
    @Size(min = 8, max = 16)
    @Transient
    private String password;

    public byte[] getHashedPassword() {
        return this.hashedPassword;
    }

    protected void setHashedPassword(byte[] hashedPassword) {
        this.hashedPassword = hashedPassword;
    }

    public void setPassword(String password) {
        this.password = password;
        this.setHashedPassword(this.hashAndSaltMyPassword(this.password));
    }

    protected String getPassword() {
        return this.password;
    }

    protected byte[] hashAndSaltMyPassword(String password) {
        ...
    }
}

完成。

谢谢,这很有意义,但限制了我验证密码的能力。我有一个自定义密码验证器,放在类级别。这是类级别的,因为我必须进行跨域验证。具体来说,我需要验证密码与用户名不同。因此,我认为我需要有一个@Transient password字段,用于在自定义验证器中存储用于验证的明文密码。如果我能使用简单的字段级注释,你的建议就行了。关于如何通过类级验证注释实现您所描述的内容,您有什么想法吗?您已经描述了您的答案,但我还是编辑了答案。总之,将明文密码存储在
@Transient
字段中,添加类级约束,并编写受保护的getter。我建议在同一个包中定义验证器。好的,谢谢。我成功地做到了这一点,而不需要getPassword()的受保护版本。如果明文pwd不为null,则返回明文pwd(仅在创建新用户或更改pwd时为null)。如果明文pwd为null,则返回哈希版本。是的,我考虑过将验证器放在同一个包中,但希望不会污染实体包空间,但考虑到实体和验证器的紧密耦合,这似乎越来越是正确的做法。感谢更新答案。希望这对其他人也有用。我认为这是正确的答案,因为这确实是我成功的原因。
@Entity
@MyCustomConstraint(...)
public class User implements Serializable {

    // place persistence annotations here, for example
    @Lob @Column(...)
    private byte[] hashedPassword;
    // place validation constraints here, for example
    @Size(min = 8, max = 16)
    @Transient
    private String password;

    public byte[] getHashedPassword() {
        return this.hashedPassword;
    }

    protected void setHashedPassword(byte[] hashedPassword) {
        this.hashedPassword = hashedPassword;
    }

    public void setPassword(String password) {
        this.password = password;
        this.setHashedPassword(this.hashAndSaltMyPassword(this.password));
    }

    protected String getPassword() {
        return this.password;
    }

    protected byte[] hashAndSaltMyPassword(String password) {
        ...
    }
}