首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Spring数据REST中实体验证后运行@HandleBeforeCreate

在Spring数据REST中实体验证后运行@HandleBeforeCreate
EN

Stack Overflow用户
提问于 2015-10-11 12:34:19
回答 1查看 2.5K关注 0票数 9

我使用Spring来持久化我的User实体

代码语言:javascript
复制
@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
}

使用存储库:

代码语言:javascript
复制
public interface UserRepository extends CrudRepository<User, Long> {}

我要做的是首先验证POSTed用户:

代码语言:javascript
复制
@Configuration
public class CustomRestConfiguration extends SpringBootRepositoryRestMvcConfiguration {

    @Autowired
    private Validator validator;

    @Override
    protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        validatingListener.addValidator("beforeCreate", validator);
    }

}

直到稍后,在将用户密码存储在DB中之前,才对其进行散列:

代码语言:javascript
复制
@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先执行验证,然后才散列密码?我知道我可以自己编写控制器,并以细粒度的方式指定所有内容,但我宁愿把它作为我的最后手段。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-10-11 17:56:04

正如我在调试器中所调查的那样,传入的实体按以下顺序处理:

  1. Spring在SpringValidatorAdapter::validate中反序列化JSON时执行bean验证。这里的密码是纯文本。
  2. 将调用@HandleBeforeCreate并对密码进行散列。
  3. JPA在将其保存到DB中之前执行实体验证。这里的密码已经散列,验证失败。在我的例子中(JPA的Hibernate实现),验证是在BeanValidationEventListener::validate中执行的。

解决方案1(两个阶段的完全验证)

我发现的一种解决方案是只使用@NotEmpty来放松对JSON字段的约束(这样两个验证阶段都通过了,而传入的JSON仍然被检查为空/无效),并在@HandleBeforeCreate中执行原始密码的大小验证(并在需要时从那里抛出适当的异常)。

这个解决方案的问题是,它要求我编写自己的异常处理程序。为了跟上Spring在错误响应主体方面设置的高标准,我必须为这个简单的例子编写大量代码。实现这一目的的方法被描述为这里

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

正如博赫斯拉夫·伯格哈特所暗示的,可以禁用JPA完成的第二个验证阶段。这样,您可以保留min和max约束,同时避免编写任何额外的代码。和往常一样,这是简单和安全之间的一种权衡。禁用JPA的方法被描述为这里

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

另一种解决方案,至少在我的情况下是有效的,是保持最大密码长度无限制。这样,在第一个验证阶段,将检查密码是否太短,而在第二个阶段,每次都有效地验证密码(因为加密的密码已经足够长)。

对此解决方案的唯一警告是,@Size(min = 5)似乎没有检查无效,所以我不得不添加@NotNull来处理这种情况。总之,该字段注释为:

代码语言:javascript
复制
@NotNull
@Size(min = 5)
private String password;
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33065008

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档