Java 在Spring Boot Data REST中实体验证后运行@HandleBeforeCreate
我正在使用SpringBootDataREST来持久化我的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
用户
实体
@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先执行验证,然后再散列密码?我知道我可以自己编写一个控制器,并以细粒度的方式指定所有内容,但我宁愿把它作为我的最后手段。当我在调试器中调查时,发现传入实体按以下顺序处理:
SpringValidatorAdapter::validate
中反序列化JSON时执行bean验证。这里的密码是纯文本的@handlebeforreacreate
被调用,密码被散列BeanValidationEventListener::validate
中执行的@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 Boot1.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;