In a web application that uses Data JPA with Hibernate, we use the web pagination functionality to provide swap and sort capabilities into various object lists.
@Controller public class MyEntityController { @RequestMapping(method = RequestMethod.GET) public ModelAndView list(Pageable pageable) { ... } } @Configuration public class MyWebMvcConfig extends WebMvcConfigurationSupport { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { super.addArgumentResolvers(argumentResolvers); argumentResolvers.add(new PageableArgumentResolver()); } } public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, String> { Page<MyEntity> findByPropertyX(String propertyX, Pageable pagable); }
This allows you to define the properties of the entity in the rendered html as a special sorting of the query parameters , where the page.sort
value really matches the property in the entity on which you want to sort.
<table> <thead> <tr> <th><a href="?page.sort=propertyX&page.sort.dir=asc">Property X</a></th> <th><a href="?page.sort=propertyY&page.sort.dir=asc">Property Y</a></th> </tr> </thead> <tbody>...</tbody> </table>
This creates a resulting URL, for example:
http://host/context-root/entities/?page.sort=propertyX&page.sort.dir=asc
The problem is that users can change the URL to use invalid page.sort
properties that refer to non-existent column / property names or, even worse, to use invalid JPA query characters that result in invalid syntax.
For example, if the URL is changed to sort by "noSuchProperty":
http://host/context-root/entities/?page.sort=noSuchProperty&page.sort.dir=asc
But this property does not exist, the following exception will be thrown:
java.lang.IllegalArgumentException: No property noSuchProperty found for type class com.my.company.MyEntity at org.springframework.data.repository.query.parser.Property.<init>(Property.java:76) . . . at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:86) . . . at $Proxy68.findByPropertyX(Unknown Source) at com.my.company.MyEntityRepository.findByPropertyX(MyEntityRepository.java:17
Similarly, if the URL is changed to an invalid request syntax character, for example, "":
http://host/context-root/entities/?page.sort=%22&page.sort.dir=asc
The following error will occur:
java.lang.StackOverflowError java.util.regex.Pattern$GroupTail.match(Pattern.java:4227) . . . org.springframework.data.repository.query.parser.Property.create(Property.java:326) org.springframework.data.repository.query.parser.Property.create(Property.java:326) org.springframework.data.repository.query.parser.Property.create(Property.java:326) org.springframework.data.repository.query.parser.Property.create(Property.java:326)
(There is also a third flavor of exceptions, which leads to org.hibernate.QueryException
when @Query
explicitly defined in the repository method.)
Spring JPA data abstracts the details of sorting, swapping and processing these parameters; however, it does not seem to be able to handle these scripts (i.e., where an invalid sort parameter is specified).
We could add some additional user logic to verify that the sort property really exists in the entity; However, I am wondering if there is a cleaner, more centralized approach for this so that we do not lose the advantages and simplicity of Spring JPA data abstractions. We use this sorting function in our application with many different objects, so ideally we need a more general approach, instead of explicitly defining or checking the sorting properties for each requested page of the object.
In particular, we actually extend PageableArgumentResolver
to accept the default annotated sort value that is presented in our controller (not shown in the code examples for simplicity), so we would like to simply discard this default sort order, or just the default sort order for object, not an exception.
Some ideas and attempts. I could use a QueryCreationListener
to intercept the creation of the query and get the sort parameter; however, I cannot change the request at this point. Or I can extend and use a custom PageableArgumentResolver
(we are already doing this) to capture the sorting options; however, I do not have access to the object at that time, and also can not determine whether the object really has a property under this name. We can declare supported properties explicitly; however, again, this defeats the idea of ββcentrally and automatically processing this scenario without requiring specific or declared knowledge of entities.
Is there any other type of interceptor or similar design that I can use to centrally check page sorting options and, if necessary, change them before invoking the request? Or is there some kind of configuration or way that Spring can automatically process this script so that it more elegantly handles invalid sorting options?