我使用Spring来持久化我的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> {}我要做的是首先验证POSTed用户:
@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先执行验证,然后才散列密码?我知道我可以自己编写控制器,并以细粒度的方式指定所有内容,但我宁愿把它作为我的最后手段。
发布于 2015-10-11 17:56:04
正如我在调试器中所调查的那样,传入的实体按以下顺序处理:
SpringValidatorAdapter::validate中反序列化JSON时执行bean验证。这里的密码是纯文本。@HandleBeforeCreate并对密码进行散列。BeanValidationEventListener::validate中执行的。解决方案1(两个阶段的完全验证)
我发现的一种解决方案是只使用@NotEmpty来放松对JSON字段的约束(这样两个验证阶段都通过了,而传入的JSON仍然被检查为空/无效),并在@HandleBeforeCreate中执行原始密码的大小验证(并在需要时从那里抛出适当的异常)。
这个解决方案的问题是,它要求我编写自己的异常处理程序。为了跟上Spring在错误响应主体方面设置的高标准,我必须为这个简单的例子编写大量代码。实现这一目的的方法被描述为这里。
解决方案2 (Spring验证没有JPA实体验证)
正如博赫斯拉夫·伯格哈特所暗示的,可以禁用JPA完成的第二个验证阶段。这样,您可以保留min和max约束,同时避免编写任何额外的代码。和往常一样,这是简单和安全之间的一种权衡。禁用JPA的方法被描述为这里。
解决方案3(只保留最小密码长度约束)
另一种解决方案,至少在我的情况下是有效的,是保持最大密码长度无限制。这样,在第一个验证阶段,将检查密码是否太短,而在第二个阶段,每次都有效地验证密码(因为加密的密码已经足够长)。
对此解决方案的唯一警告是,@Size(min = 5)似乎没有检查无效,所以我不得不添加@NotNull来处理这种情况。总之,该字段注释为:
@NotNull
@Size(min = 5)
private String password;https://stackoverflow.com/questions/33065008
复制相似问题