Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/366.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在Spring Boot Data REST中实体验证后运行@HandleBeforeCreate_Java_Spring_Validation_Spring Data Rest - Fatal编程技术网

Java 在Spring Boot Data REST中实体验证后运行@HandleBeforeCreate

Java 在Spring Boot Data REST中实体验证后运行@HandleBeforeCreate,java,spring,validation,spring-data-rest,Java,Spring,Validation,Spring Data Rest,我正在使用SpringBootDataREST来持久化我的用户实体 @Entity public class User { @Id @GeneratedValue private long id; @NotEmpty private String firstName; @NotEmpty private String lastName; @NotEmpty private String email; @Si

我正在使用SpringBootDataREST来持久化我的
用户
实体

@Entity
public class User {

    @Id
    @GeneratedValue
    private long id;

    @NotEmpty
    private String firstName;

    @NotEmpty
    private String lastName;

    @NotEmpty
    private String email;

    @Size(min = 5, max = 20)
    private String password;

    // getters and setters
}
使用存储库:

public interface UserRepository extends CrudRepository<User, Long> {}
仅在以后将用户密码存储到数据库之前对其进行散列:

@Component
@RepositoryEventHandler(User.class)
public class UserRepositoryEventHandler {

    private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @HandleBeforeCreate
    public void handleUserCreate(User user) {
         user.setPassword(passwordEncoder.encode(user.getPassword()));
    }
}
但事实证明,验证是在密码散列之后执行的,因此,由于散列后的密码太长,验证失败


有没有办法指示Spring先执行验证,然后再散列密码?我知道我可以自己编写一个控制器,并以细粒度的方式指定所有内容,但我宁愿把它作为我的最后手段。

当我在调试器中调查时,发现传入实体按以下顺序处理:

  • Spring在
    SpringValidatorAdapter::validate
    中反序列化JSON时执行bean验证。这里的密码是纯文本的
  • @handlebeforreacreate
    被调用,密码被散列
  • JPA在将实体保存到数据库之前执行实体验证。此处的密码已哈希,验证失败。在我的例子(JPA的Hibernate实现)中,验证是在
    BeanValidationEventListener::validate
    中执行的
  • 解决方案1(两个阶段的完全验证)

    我发现的一个解决方案是通过使用
    @NotEmpty
    (这样两个验证阶段都通过了,并且仍然检查传入的JSON是否为空/空)来放松对
    密码
    字段的限制,并在
    @handlebeforreate
    中执行原始密码的大小验证(如果需要,从那里抛出适当的异常)

    这个解决方案的问题是,它要求我编写自己的异常处理程序。为了跟上Spring Data REST关于错误响应体设置的高标准,我必须为这个简单的案例编写大量代码。下面介绍了实现方法

    解决方案2(没有JPA实体验证的SpringBean验证)

    正如所暗示的,可以禁用JPA完成的第二个验证阶段。这样,您可以保留最小和最大约束,同时避免编写任何附加代码。一如既往,这是简单性和安全性之间的权衡。禁用JPA的方法如下所述

    解决方案3(仅保留最小密码长度约束)

    另一个解决方案,至少在我的案例中是有效的,就是让最大密码长度不受限制。这样在第一个验证阶段检查密码是否太短,在第二个阶段每次都有效地验证密码(因为加密密码已经足够长了)

    此解决方案的唯一警告是,
    @Size(min=5)
    似乎没有检查空值,因此我必须添加
    @NotNull
    来处理此情况。总之,字段被注释为:

    @NotNull
    @Size(min = 5)
    private String password;
    

    您使用的是哪个版本的Spring Boot/Data REST?我刚刚在我的应用程序上测试了这个,当我在Repo处理程序中放置断点并
    SizeValidatorForCharSequence
    时,验证程序中的断点在处理程序之前被命中,所以对我来说它可以正常工作。我使用的是Spring Boot 1.2。5@BohuslavBurghardt我使用的是Spring Boot
    1.2.6。发布
    它管理所有其他依赖版本。我真的不知道如何在验证器上设置断点,因为我自动连接了由JPA创建的验证器(我想)。它发生在幕后的某个地方。我会尝试这样做though@Wojtek,在成功的bean验证之后(在持久化实体之前),是否还有其他钩子可以用来获得控制权?@masT不正是您所需要的吗?它在验证之后和持久化之前被调用。或者您的意思是在JPA进行实体验证之后?我已经有一段时间没有使用Spring或JPA了,所以我不能真正回答您的问题。@Wojtek,在我的情况下,我需要执行的不是验证,而是支持为一些其他属性(将从另一个存储库获取)添加值,以便查找任何钩子post验证来执行此处理。有趣的阅读。它对我有效的原因可能是因为我通过application.properties禁用了Hibernate验证(
    spring.jpa.properties.javax.persistence.validation.mode=none
    )。您也可以这样做,然后只执行第1步反序列化期间的验证。是的,我读过,但我真的不想放弃所有JPA验证功能:)将其添加为另一个可能的解决方案。谢谢你对博胡斯拉夫的帮助!用正则表达式验证字符串怎么样?
    @NotNull
    @Size(min = 5)
    private String password;