Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring session based javax constraint validation

What I'm trying to accomplish is to access my Spring Session within a custom constraint

Sample scenario:

Custom constraint @UniqueEmail verifies that the email is not already in use in the system. This validation is performed on the edit endpoint and should be ignore if the user didn't change the email, 1st: because there's no need for it and 2nd: because querying the db for that email will actually return a result, which is the user itself, although there's no way of telling it without accessing the session

This works:

If I use model attributes with custom editor though @InitBinder I can set a property in the to-be-validated bean before the validation occurs like so

@InitBinder(value="myModelObj")
protected void initBinder(WebDataBinder binder, HttpSession session) {
    User user = (User) session.getAttribute("user");
    binder.registerCustomEditor(User.class, "user", new UidPropertyEditor(user));
}

@RequestMapping(...)
public String updateUser(@Valid @ModelAttribute("myModelObj") MyModelObj form){
    ...
}

MyModelObj has an attribute which will be replaced with the actual session user. Problems:

  1. There must be a property in the bean to hold the user, even though it is not editable through the web form
  2. The web form must submit this property as well, in my case using an input[type="hidden"] field (user can change it at will, we never trust what the user sends)

This does not work

The new endpoints have to use @RequestBody rather than @ModelAttribute, which means that (afaik) @InitBinder won't work anymore, hence losing access to the session object.

How (if possible) can I access the session from within the custom constraint?

public class EmailIsUniqueStringValidator implements ConstraintValidator<EmailIsUnique, String> {

@Autowired
private UserDAO userDAO;

HttpSession session; //Somehow initialized

@Override
public void boolean isValid(String email, ConstraintValidatorContext context) {
    User user = (User) session.getAttribute("user");
    if(user.getEmail().equals(email)){
        return true; // No need to validate
    }
    else if(userDAO.emailInUse(email)) {
        return false;
    }
}

Non-ideal approach:

What I'm doing now is performing the session-dependant validations in the controller manually, which means I have 2 points where validation is performed.

There are some other interesting options in this post too, but if there was a way to access the session...

Thanks in advance

like image 519
Hartimer Avatar asked Jan 12 '23 21:01

Hartimer


1 Answers

This can be achieved using RequestContextHolder like so:

public class EmailIsUniqueStringValidator implements ConstraintValidator<EmailIsUnique,         String> {

@Autowired
private UserDAO userDAO;

HttpSession session;

@Override
public void boolean isValid(String email, ConstraintValidatorContext context) {
    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    session = attr.getRequest().getSession();

    User user = (User) session.getAttribute("user");
    if(user.getEmail().equals(email)){
        return true; // No need to validate
    }
    else if(userDAO.emailInUse(email)) {
        return false;
    }
}
like image 132
Hartimer Avatar answered Jan 18 '23 07:01

Hartimer