Running #HandleBeforeCreate after entity validation in Spring Boot Data REST - java

I'm using Spring Boot Data REST to persist my User entities
#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 the repository:
public interface UserRepository extends CrudRepository<User, Long> {}
What I want to do is validate the POSTed users first:
#Configuration
public class CustomRestConfiguration extends SpringBootRepositoryRestMvcConfiguration {
#Autowired
private Validator validator;
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
}
}
and only later hash the user's password before storing it in the 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()));
}
}
As it turns out though, validation is performed after the password hashing and as a result it fails due to the hashed password being too long.
Is there any way to instruct Spring to perform validation first and only then hash the password? I know I could write a controller myself and specify everything in a fine-grained manner, but I'd rather leave it as my last resort.

As I investigated in the debugger it turned out that an incoming entity is processed in the following order:
Spring performs bean validation when deserializing JSON in SpringValidatorAdapter::validate. The password here is in plain text.
#HandleBeforeCreate is invoked and the password is hashed.
JPA performs entity validation before saving it in the DB. The password here is already hashed and validation fails. In my case (Hibernate implementation of JPA) the validation was performed in BeanValidationEventListener::validate.
Solution 1 (full validation in both phases)
One solution I found was to relax the constraint on the password field by just using #NotEmpty (so that both validation phases passed and still the incoming JSON was checked for emptiness/nullity) and perform the size validation of the raw password in #HandleBeforeCreate (and throw appropriate exception from there if needed).
The problem with this solution is that it required me to write my own exception handler. To keep up with the high standards set by Spring Data REST with respect to error response body, I would have to write lots of code for this one simple case. The way to do this is described here.
Solution 2 (Spring bean validation without JPA entity validation)
As hinted by Bohuslav Burghardt, it is possible to disable the second validation phase done by JPA. This way you can keep the min and max constrains and at the same time avoid writing any additional code. As always it's a trade-off between simplicity and safety. The way to disable JPA is described here.
Solution 3 (preserving only min password length constraint)
Another solution, at least valid in my case, was to leave the max password length unbounded. This way in the first validation phase the password was checked whether it wasn't too short and in the second phase it effectively validated every time (because encrypted password was already long enough).
The only caveat to this solution is that #Size(min = 5) does not seem to check for nullity so I had to add #NotNull to handle this case. All in all the field is annotated as:
#NotNull
#Size(min = 5)
private String password;

Related

Can Java Validator method validateProperty validate object with multiple fields inside?

As stated in the title, I'm using the Validator to validate fields based on their names like this:
mandatoryInputs.stream()
.map(x -> v.validateProperty(accountBenefitForm, x, AccountBenefitFormAdditionalInfo.class))
it works fine, but only for the simple fields like Strings that have their constraints in the accountBenefitForm for example:
#NotBlank(message = "Username can not be null.", groups = {AccountBenefitFormBasicInfo.class})
#Size(max = 255, message = "Username is too long (max size 255).")
private String username;
But it won't work for objects that have multiple fields inside them, like this one:
#Valid
private ContactData contactData;
where ContactData implementation looks like this:
#NotBlank(message = "You have to add e-mail address.", groups = {AccountBenefitFormAdditionalInfo.class})
#Email(message = "E-mail is not valid.", groups = {AccountBenefitFormAdditionalInfo.class})
#Size(max = 255, message = "E-mail is too long (max size 255).", groups = {AccountBenefitFormAdditionalInfo.class})
private String email;
#NotBlank(groups = {AccountBenefitFormAdditionalInfo.class})
private String phoneNumber;
Is there a way I can make this work or do I need to validate those more complex objects on their own?
You have basically two kinds of annotations that can be used for validations here: Spring annotations (#Validated) as well as the javax annotation (#Valid, #NotBlank) etc.
For Spring, you can luckily often skip the manual validation unless you have some custom constraints (e.g. if person lives in country ABC, they need to provide additional info). Annotating just the field is not enough if you don't cascade the validation from the outer class. This cascade can be done conveniently on method-level by annotating the method param with #Valid e.g.
void doSomething(#Valid ContactDataHolder contactDataHolder) { ... }
If you'd like to use validation in Spring, I would recommend to use the Spring Validator interface instead of the one from javax as it should give you the expected behavior for nesting. You might also decide to apply #Validated on the class level to save you from writing #Valid(ated) on the method level each time.
So I've managed to somewhat resolve my problem by using the Apache BVal. Heres the code to create a validator to use the validateProperty method with cascading validation enabled:
ValidatorFactory factory = Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory();
CascadingPropertyValidator validator = factory.getValidator().unwrap(CascadingPropertyValidator.class);
validator.validateProperty(accountBenefitForm, x, true, Default.class, AccountBenefitFormAdditionalInfo.class))
where x is the string of field to validate, and if that field is annotated with #Valid, it will then validate the inside fields according to their own constraints.
Along the way I've also found out that you can just use the "normal" javax Validator and pass the field to validate as contactData.email which means validate email field of the contactData field of the object that u pass as first argument to the validateProperty method.
Edit:
BVal supports Bean Validation 1.1 (JSR 349) and not the 2.0 version(JSR 380), and since #NotBlank or #NotEmpty constrains are part of 2.0, it won't validate a field annotated with them. Here are the docs for the 1.1 , and 2.0

how to address "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" with Spring controllers that use Converters

I'm in the middle of migrating a project from:
Spring 4.2.1 -> 5.0.0
Spring Data Gosling -> Kay
Hibernate 4.3.8 -> 5.8.0
And I'm running getting "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" when accessing an object coming from my database in a controller method.
Here's a stripped down version of my code:
// CustomUser.java
#Entity
#Access(AccessType.FIELD)
#Table(name = "users")
public class CustomUser implements Serializable {
...
#Id
#GeneratedValue//details omitted
#GenericGenerator//details omitted
#Column(name = "id", insertable = true, updatable = true, unique = true, nullable = false)
private Long id;
#Column(name = "name")
private String name;
public String getName() { return name; }
}
// UserController.java
#RequestMapping(value = "/user/{userId}/", method = RequestMethod.GET)
public String showUser(#PathVariable("userId") CustomUser user) {
System.out.println("user name is [" + user.getName() + "]");
return "someTemplate";
}
// UserService.java
#Service
public class UserServiceImpl implements UserService {
#Autowired UserRepository userRepository;
#Override
public User findUserById(Long userId) {
return userRepository.getOne(userId);
}
}
// UserRepository.java
public interface UserRepository extends JpaRepository<CustomUser, Long> { }
// UserConverter.java
#Component
public class UserConverter implements Converter<String, CustomUser> {
#Autowired UserService userService;
#Override
public CustomUser convert(String userId) {
CustomUser user = userService.findUserById(SomeUtilClass.parseLong(userId));
return user;
}
}
There's also a #Configuration WebMvcConfigurerAdapter class that autowires a UserConverter instance and adds it to a FormatterRegistry.
Prior to starting this upgrade, I could hit:
http://server:port/user/123/
and Spring would take the "123" string, the UserConverter::convert method would fire and hit the Postgres database to look up a user with that id, and I'd get back a CustomUser object in my controller's "showUser" method.
But, now I am getting the org.hibernate.LazyInitializationException. This is occurring when I attempt to print out the user's name in the "showUser" method - or even just "println(user)" without accessing a field.
Most of the info I've been able to turn up from searching suggests that this exception comes from having an object having a lazily loaded collection of sub objects (like if my CustomUser had a collection of Permission objects or something that mapped to a different database table). But in this case I'm not even doing that, this is just a field on the object.
My best guess at the moment is this is due to some kind of hibernate session being terminated after the Converter does its work, so then back in the controller I don't have a valid session. (although again, I don't know why the CustomUser object is coming back unusable, I'm not attempting to fetch a subcollection).
I have added the Hibernate annotation "#Proxy(lazy = false)" to my CustomUser.java and if I do that the problem goes away. But, I'm not sure this is a good solution - for performance reasons, I really don't think I want to go down the road of eagerly fetching EVERYTHING.
I've also tried annotating various things (the service method, the controller method, etc.) with #Transactional; I haven't gotten that to work but I am still reasonably new to Spring and I may be trying that in the wrong place or misunderstanding what that should do.
Is there a better way to handle this than just "#Proxy(lazy = false)" on all of my Entity classes?
The immediate problem comes from the use of userRepository.getOne(userId). It is implemented in the SimpleJpaRepository using EntityManager.getReference. This method returns just a Proxy, which only contains its id. And only when a property gets accessed those get lazy loaded. This includes simple properties like name in your case.
The immediate fix is to use findOne which should load all the eager properties of your entity which should include simple properties, so the exception goes away.
Note that this will slightly change the behavior of your converter. The current version will not fail when the id is not existent in the database because it never checks. The new one will obtain an empty Optional and you'll have to convert it to null or throw an exception.
But there is (or might be) a bigger problem hiding: The entity is still detached because in absence of an explicit transaction demarcation the transactions only span single repository calls. So if you later want to access lazy loaded properties, you might get the same problem again. I see various options to consider:
Leave it as it is, being aware that you must not access lazy loaded properties.
Make sure a transaction is started before the converters get invoked.
Mark your controllers with #Transactional and loading the user (again) in the controller.

Java server side validation for HTML and other invalid inputs

How can I prevent user from entering HTML or Java script tags in input type in Spring MVC? There should be a server side validation. I am working on a project with thousands of JSPs and controllers. How can I do this?
If you want a server side solution, you could implement a redirect filter that eliminates everything that contains javascript tags and javascript code. Another way is to check the input values in the controller's method that is associated with it.
You probably have to redesign a few things. First, you should always validate the user input twice: once client-side, once server-side.
Thus, you will need to validate the user input in your JavaScript code (using a Regexp probably), and to validate it again in your Java code.
If your application follow the usual design patterns, your controller receives a DTO as a parameter to the entry-point. There you can use the #Valid annotation and add all the necessary rules on the fields of your DTO (using javax.validation annotations).
While there may be many possible answers, one of them is using JSR 303 validator framework.
You can include hibernate validator to use JSR 303 framework.
First step is applying different type of constraint on your class. For example
example taken from : Hibernate Validator - Reference - 1.2. Applying constraints
package org.hibernate.validator.referenceguide.chapter01;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Car {
#NotNull //manufacturer must never be null
private String manufacturer;
#NotNull
#Size(min = 2, max = 14) //licensePlate must never be null and must be between 2 and 14 characters long
private String licensePlate;
#Min(2)
private int seatCount; //seatCount must be at least 2
//getters and setters ...
}
Now in your controller, use #Valid annotation to validate your car object and also pass a BindingResult parameter, that will validate whether this object is valid or not
#Controller
#RequestMapping("/car")
public class CarController {
#RequestMapping(value = "/newcar", method = RequestMethod.POST)
public String addCustomer(#Valid Car car, BindingResult result) {
if (result.hasErrors()) {
//car data is not valid, enter data again
return "AddNewCar.jsp";
} else {
//save car logic here
return "CarSavedSuccessfully.jsp";
}
}
}

How to use different validation rules on same entity in Hibernate?

Problem:
How to save object Account as nested object when only ID is needed without getting ConstraintValidator exception?
Problem is because i have set validation rules to class, but when i want to save sem entity as nested object i get exception that some property values are missing. So i would liek to have different validation rules when i want to persist object as a whole and when i want to use it only sa nested object (when only ID is needed).
public class Account {
private int id;
#NotNull
private String name;
#NotNull
private String lastName;
#NotNull
private String userName;
//getters&setters
If I include Account as nested object i just need ID to be able to use it as FK (account entity is already in DB), but because of #NotNull annotation i get Exception.
Is there a way to ignore those annotations from Account when trying to save object Shop or how to create different validation rules for Account to validate just soem other properties and not all?
public class Shop {
private int id;
private Account owner; // only ID is needed
Do you have any basic example? I dont understand those in documentation. I have already read documentation before posting here.
You want to look at Bean Validation groups where you can classify specific validations so they are only activated when that group is validated and ignored otherwise.
You can refer to the documentation here for details.
Taking an example from the documentation:
// This is just a stub interface used for tagging validation criteria
public interface DriverChecks {
}
// The model
public class Driver {
#Min(value = 18, message = "You must be 18", groups = DriverChecks.class)
private int age;
// other stuffs
}
A group is nothing more than a tag that allows you to enable/disable validations based on specific use cases at run-time. By not specifying the groups attribute on a bean validation annotation, it defaults to the Default group, which is what Bean Validation uses if a group-tag isn't specified at the time of validation.
That means the following holds true:
// Age won't be validated since we didn't specify DriverChecks.class
validator.validate( driver );
// Age will be validated here because we specify DriverChecks.class
validator.validate( driver, DriverChecks.class );
This works great when you're triggering the validation yourself inside your service methods because you can manually control which group checks are applicable based on that method's use case.
When it comes to integrating directly with Hibernate ORM's event listeners that can also trigger bean validation, group specifications become a bit harder as they must be specified based on the event-type raised by hibernate.
javax.persistence.validation.group.pre-persist
javax.persistence.validation.group.pre-update
javax.persistence.validation.group.pre-remove
For each of the above properties you can specify in the JPA properties supplied to Hibernate, you can list a comma delimited list of groups that are to be validated for each of those event types. This allows you to have varying checks during insert versus update versus removal.
If that isn't sufficient, there is always the fact that you can create your own constraint validator implementation and annotation to plug into Bean Validation and specify that at the class or property level.
I have often found this useful in cases where values from multiple fields must be validated as a cohesive unit to imply their validity as the normal field-by-field validations didn't suffice.

Validating Jpa Entities: In service or by lifecycle listeners

The question is where it is better (or in other words: where do you prefer) to put business validation logic of Jpa Entities.
Two ideas are:
In the EntityListener that before save or update would validate the entity
In the service that provides access to jpa persisting methods.
There are pros and cons of both.
When using approach No. 2 it is easier to test as you may just mock the jpa provider and test the validation logic. On the other hand with approach No. 1 the validation would happen at the same moment with validations like #NotNull etc.
I would love to know how do you solve validations in your projects and which is the better way to go.
Thanks.
Here's a general thumb rule that I follow:
When using bean validation, specify
rules that do not require dependencies
on other beans. The moment you depend
on another bean, get your service
layer to handle that dependency.
In other words, if you have a reference to a bean inside another, avoid putting in that #NotNull constraint. Your service layer is best used for that, for you're catching the violation much earlier, and at a more logical point (since other business validations would assume that the beans are available).
As an example, consider the following entity (apologies for it wont compile)
#Entity
public class User
{
#Id
private int id;
#NotNull
private String fullName;
#NotNull
private String email;
private Set<Role> roles; //No bean validation constraints here.
...
public boolean mapRoleToUser(Role role)
{ //Validation is done here. Including checks for a null role.
}
}
#Entity
public class Role
{
#Id
private int id;
#NotNull
private String name;
}
The service layer in this case, is the one that should validate whether the user has a role attached or not. Verification in the pre-persist or pre-update phase is a bit too late, especially when there is a distinct service layer that has business logic, and the rest of the business logic in the domain model (sadly, I haven't seen a good enough application with all of the logic in the domain model alone).

Categories

Resources