我正在使用Spring Boot Data REST来保留我的User
实体
@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> {}
我想要做的是首先验证POST
ed用户:
@Configuration
public class CustomRestConfiguration extends SpringBootRepositoryRestMvcConfiguration {
@Autowired
private Validator validator;
@Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
}
}
并且稍后在将用户密码存储到DB中之前对其进行哈希处理:
@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先执行验证,然后才对密码进行哈希处理?我知道我可以自己编写一个控制器并以细粒度的方式指定所有内容,但我宁愿把它作为我的最后手段。
答案 0 :(得分:3)
正如我在调试器中调查的那样,结果是传入的实体按以下顺序处理:
SpringValidatorAdapter::validate
中反序列化JSON时执行bean验证。这里的密码是纯文本。@HandleBeforeCreate
被调用,密码被哈希。BeanValidationEventListener::validate
。解决方案1(两个阶段都完全验证)
我找到的一个解决方案是通过仅使用password
来放宽@NotEmpty
字段上的约束(以便两个验证阶段都通过,并且仍然检查传入的JSON是否为空白/无效)并执行@HandleBeforeCreate
中原始密码的大小验证(如果需要,从那里抛出适当的异常)。
这个解决方案的问题是它需要我编写自己的异常处理程序。为了跟上Spring Data REST在错误响应体方面设置的高标准,我必须为这个简单的案例编写大量代码。这样做的方法是here。
解决方案2(没有JPA实体验证的Spring bean验证)
如Bohuslav Burghardt所示,可以禁用JPA完成的第二个验证阶段。这样,您可以保持最小和最大约束,同时避免编写任何其他代码。一如既往,它是简单性和安全性之间的权衡。有关禁用JPA的方法,请参阅here。
解决方案3(仅保留最小密码长度限制)
另一种解决方案,至少在我的情况下有效,是将最大密码长度保持为无限制。这种方式在第一个验证阶段,密码被检查是否它不是太短,在第二阶段它有效地验证每次(因为加密密码已经足够长)。
此解决方案的唯一警告是@Size(min = 5)
似乎没有检查无效,所以我必须添加@NotNull
来处理这种情况。总而言之,该领域的注释为:
@NotNull
@Size(min = 5)
private String password;