Question

I have a Spring MVC controller that handles requests to do with user management including a method to save user details and a method to allow a user to reset their password. I want to use a validator to ensure that the user typed the same password twice.

My controller

@Controller
public class UserDetails {
...
@InitBinder
public void binder(WebDataBinder binder) {
    binder.addValidators(new PasswordValidator());
}
...
@RequestMapping(value="/saveUserDetails", method=RequestMethod.POST)
public String saveUserDetails(
    @ModelAttribute User user) {
    ...
}
...
@RequestMapping(value="/resetPassword", method=RequestMethod.POST)
public String resetPassword(
    @Validated PasswordPair password, BindingResult result) {
    ...
}

And the Validator

    private final static class PasswordValidator implements Validator {

    private final static int MIN_LEN=5;

    @Override
    public boolean supports(Class<?> clazz) {
        return PasswordPair.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        PasswordPair pair = (PasswordPair)target;
        //1 impose password rules
        if (pair.getPassword().length()<MIN_LEN) {
            errors.rejectValue("password", "Too short", "Password must bne at least "+MIN_LEN+" chars");
        }
        if (!pair.getPassword().equals(pair.getConfirmPassword())) {
            errors.rejectValue("confirmPassword", "mustMatch", "passwords must match");
        }
    }

  }

The problem is that when saveUserDetails is called spring is trying to validate the user with the password validator.

I have tried changing @InitBinder to @InitBinder("password") or @InitBinder("PasswordPair") but in that case nothing is validated at all.

How can I make it validate only the correct parameters?

Was it helpful?

Solution

Your approach of using @InitBinder(withname) should work , my guess is that you have just provided the wrong name.

Can you try this name:

@InitBinder("passwordPair")

Another option will to explicitly give your ModelAttribute a name, this way and use that name in the InitBinder:

public String resetPassword(
    @ModelAttribute("password") @Valid PasswordPair password, BindingResult result) {
    ...
}

@InitBinder("password")

On a related note, I have a similar issue recorded with Spring Jira and have a pull request to change this behavior in place. Please vote it up if possible - https://jira.springsource.org/browse/SPR-11429

OTHER TIPS

Add password and confirmPassword field to the User object instead of having PasswordPair object. Change resetPassword method to have user as inparameter. And change the validator to validate the user. Here i suppose that you would have password validation even when create a new user?

Ex:

 @Controller
 public class UserDetails {

 @InitBinder
 public void binder(WebDataBinder binder) {
    binder.addValidators(new PasswordValidator());
 }

 @RequestMapping(value="/saveUserDetails", method=RequestMethod.POST)
 public String saveUserDetails(@ModelAttribute User user) {
    ...
 }

 @RequestMapping(value="/resetPassword", method=RequestMethod.POST)
 public String resetPassword(@Valid @ModelAttribute User user, BindingResult result) {
    ...
 }

And the Validator

private final static class UserValidator implements Validator {

private final static int MIN_LEN=5;

@Override
public boolean supports(Class<?> clazz) {
    return PasswordPair.class.isAssignableFrom(clazz);
}

@Override
public void validate(Object target, Errors errors) {
    User user = (User)target;
    //1 impose password rules
    if (user.getPassword().length()<MIN_LEN) {
        errors.rejectValue("password", "Too short", "Password must bne at least "+MIN_LEN+" chars");
    }
    if (!user.getPassword().equals(user.getConfirmPassword())) {
        errors.rejectValue("confirmPassword", "mustMatch", "passwords must match");
    }
  }

}

or you could look at this post, maybe it'll help you:

Handling password confirmations on spring-mvc

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top