Spring Data Rest Override存储库(控制器与AOP)

域/库

Project { User owner; } //Querydsl repositories @RepositoryRestResource public interface ProjectRepository extends PagingAndSortingRepository, QueryDslPredicateExecutor, QuerydslBinderCustomizer { default void customize(QuerydslBindings bindings, QProject project) { (...) } } 

Requeriment:根据经过身份validation的用户上下文过滤数据:

  • 如果用户是ROLE_PUBLIC根据predicate显示项目,并且用户是owner
  • 如果用户是ROLE_ADMIN根据predicatefilter显示项目。

我尝试解决了几个替代方案:

选项1 :覆盖@RepositoryRestController如Spring DATA REST doc所述:

 @RepositoryRestController public class ProjectController { @RequestMapping(value = "/projects", method = RequestMethod.GET) @ResponseBody public PagedResources search( @QuerydslPredicate(root=Project.class ,bindings =ProjectRepository.class) Predicate predicate, @PageableDefault Pageable pageable, // @AuthenticationPrincipal Principal principal) { (...) // Update predicate wit user context projectRepository.findAll(predicate,pageagle); (...) } } 

由于@QueryDslPredicat不在RepositoryRestHandlerAdapter列表( DATAREST-838 )上,因此抛出exception:

 Failed to instantiate [com.querydsl.core.types.Predicate]: Specified class is an interface 

列表中最相似的argumentResolver是QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver但我不知道它是否对这个问题有用。

选项2 :这个问题是一个典型的跨领域问题,因此我尝试应用AOP。 定义建议并更新args(谓词):

 @Around("this(com.xxx.yyy.repository.ProjectRepository)") public void filterProjectsByUser(final ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); // .. Find args Predicate, update it adding user profile expression and procceed. pjp.proceed(args); } 

结果与默认的Repository方法不同,而不是原始的JSON对象(使用_embedded,page,_links),响应是:

 { "_links" : { "profile" : { "href" : "http://localhost:8087/api/profile/projects" } } } 

选项3使用@RestController

 @RestController public class ProjectController { @RequestMapping(value = "/projects/search", method = RequestMethod.GET) @ResponseBody public PagedResources search( @QuerydslPredicate(root=Project.class ,bindings =ProjectRepository.class) Predicate predicate, @PageableDefault Pageable pageable, // @AuthenticationPrincipal Principal principal) { (...) // Update predicate wit user context projectRepository.findAll(predicate,pageagle); (...) } } 

使用相同的路径@RequestMapping("/projects",...)也会覆盖其他端点(PUT,POST,…),这是不可取的。 这被迫定义另一个端点("projects/search") 。 我认为这种解决方法并不优雅,需要新闻端点。 我确信使用Spring Data REST存在更好的替代方案。

问题:

  • 关于如何解决这个问题的任何消息?
  • 如何将AOP应用于Spring Data REST来解决横切要求?
  • 为什么@QuerydslPredicate参数解析器?

Spring Data REST版本2.5.6

虽然已经解决,但我希望收到反馈和建议。 我希望QueryDSLPredicate注释将被修复,文档将得到改进,有关此问题的更多示例。

最后我运行选项2我的错误是没有返回proceed调用结果:

 @Aspect @Transactional @Component public class FilterProjectsAspect { @Pointcut("execution(* com.xxx.ProjectRepository.findAll(..))") public void projectFindAll() { } @Around("projectFindAll()") public Object filterProjectsByUser(final ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); for (int i = 0; i < args.length; i++) { if (args[i] instanceof Predicate) { Predicate predicate=(Predicate) args[i]; BooleanExpression isProjectOwner =buildExpressionForUser() predicate = ExpressionUtils.allOf(isProjectOwner, predicate); args[i]=predicate; //Update args } return pjp.proceed(args); } }