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
根据predicate
filter显示项目。
我尝试解决了几个替代方案:
选项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); } }