I want to add pattern matching for a user registering a password. In my user model I have:
@Column @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$") private String password;
However, when I go to register a user I am getting a 400 Bad Request.
javax.validation.ConstraintViolationException: Validation failed for classes [com.practice.models.AppUser] during persist time for groups [javax.validation.groups.Default, ]nList of constraint violations:[ntConstraintViolationImpl{interpolatedMessage='must match "^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$"', propertyPath=password, rootBeanClass=class com.practice.models.AppUser, messageTemplate='{javax.validation.constraints.Pattern.message}'}n]ntat
Can it be done with the @Pattern
annotation? And should it go on the model?
The endpoint for my controller looks like this:
@PostMapping("/register") public AppUser register(@Valid @RequestBody AppUser user) { return appUserService.createUser(user); }
This is the data I sent to register:
{ "username": "Johnny", "email": "johnny@rosevideo.com", "password": "P@ssword123", "passwordConfirmation": "P@ssword123" }
And then in my service layer, I am encrypting my password with BCrypt:
public AppUser createUser(AppUser appUser) { Optional<AppUser> existingUser = appUserRepo.findByUsername(appUser.getUsername()); if (existingUser.isPresent()) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "A user with that username already exists"); } if (!appUser.getPassword().equals(appUser.getPasswordConfirmation())) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Passwords don't match"); } String encodedPassword = bCryptPasswordEncoder.encode(appUser.getPassword()); appUser.setPassword(encodedPassword); return appUserRepo.save(appUser); } ```
Advertisement
Answer
This can often happen when you use password encryption at the same time as password validation. Basically, your app is currently running the pattern validation against the encrypted password.
Without extra configuration, the @Pattern
annotation results in validation being run not only where you use @Valid
in your controller, but also in the persistence layer when you call appUserRepo.save(appUser)
. This means the pattern matching is run against the hashed password, which may well not match your pattern.
If you don’t want validation to run at persistence, add this line to your application.properties
file:
spring.jpa.properties.javax.persistence.validation.mode=none