Running @ HandleBefore Creating After Checking an Object in Spring REST Boot Data

I am using Spring Boot Data REST to save User objects

 @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 } 

using repository:

 public interface UserRepository extends CrudRepository<User, Long> {} 

What I want to do is first check the POST ed users:

 @Configuration public class CustomRestConfiguration extends SpringBootRepositoryRestMvcConfiguration { @Autowired private Validator validator; @Override protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) { validatingListener.addValidator("beforeCreate", validator); } } 

and only later the hash password of the user before storing it in the database:

 @Component @RepositoryEventHandler(User.class) public class UserRepositoryEventHandler { private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @HandleBeforeCreate public void handleUserCreate(User user) { user.setPassword(passwordEncoder.encode(user.getPassword())); } } 

As it turned out, validation is performed after password hashing, and as a result, it fails because the hashed password is too long.

Is there a way to instruct Spring to check first and only then the hash password? I know that I could write the controller myself and specify everything in the smallest form, but I would prefer to leave it as a last resort.

+7
java spring spring-data-rest validation
source share
1 answer

As I explored in the debugger, it turned out that the incoming object is processed in the following order:

  • Spring performs a bean check on JSON deserialization in SpringValidatorAdapter::validate . The password here is in plain text.
  • @HandleBeforeCreate is called and the password is hashed.
  • JPA authenticates the object before saving to the database. The password here is already hashed, and verification is not performed. In my case (Hibernate JPA implementation) the validation was done in BeanValidationEventListener::validate .

Solution 1 (full check in both phases)

One solution I found was to loosen the restriction in the password field by simply using @NotEmpty (so that both phases of the check went through and still the incoming JSON was checked for void / invalidity) and do a raw password size check in @HandleBeforeCreate (and when need to discard the corresponding exception).

The problem with this solution is that it required me to write my own exception handler. In order to keep up with the high standards set by Spring Data REST regarding the body of the error response, I would have to write a lot of code for this simple case. A way to do this is described here .

Solution 2 (Spring bean validation without JPA entity validation)

As outlined by Bohuslav Burghardt , you can disable the second stage of verification performed by JPA. This way you can keep the min and max restrictions and at the same time avoid writing extra code. As always, this is a compromise between simplicity and security. The way to disable JPA is described here .

Solution 3 (maintaining the restriction of only the minimum password length)

Another solution, at least in my case, was to leave the maximum password length unlimited. Thus, at the first stage of the check, the password was checked whether it was not too short, and in the second phase it is checked every time (since the encrypted password is already quite long).

The only caveat for this solution is that @Size(min = 5) doesn't seem to check for nullity, so I had to add @NotNull to handle this case. In general, the field is annotated as:

 @NotNull @Size(min = 5) private String password; 
+2
source share

All Articles