QueryDslPredicateExecutor和Spring Data Rest方面的安全上下文

时间:2017-01-26 09:36:47

标签: spring-security spring-data spring-data-jpa spring-data-rest

我在Spring Data Rest的顶部构建REST API。最初是扩展JpaRepository的所有存储库。最近决定采取更灵活的方法并使用QueryDslPredicateExecutor<T>QuerydslBinderCustomizer<Q>.

几乎所有在存储库中公开的findAll方法都应该解决两个场景

  • 主体有一个角色ROLE_ADMIN,然后不应对来自PageableSort

  • 的部分应用过滤
  • principal没有角色ROLE_ADMIN我只会返回属于当前用户的那些实体

完成任务就像注释findAll方法一样简单。

@Query("select e from Entity e where e.field = ?#{principal} or 1=?#{hasRole('ROLE_ADMIN') ? 1 : 0}")
Page<Entity> findAll(Pageable pageable);

现在我希望我们的findAll与下面的内容类似

Page<Entity> findAll(Predicate predicate, Pageable pageable)

Predicate正在从请求参数构建(由@QuerydslPredicate提供),并且正在传递给RepositoryEntityController,这一切都由spring-data-rest管理,这很好。

@ResponseBody
    @RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET)
    public Resources<?> getCollectionResource(@QuerydslPredicate RootResourceInformation resourceInformation,
            DefaultedPageable pageable, Sort sort, PersistentEntityResourceAssembler assembler)
                    throws ResourceNotFoundException, HttpRequestMethodNotSupportedException {    

我想调整那个谓词(我想要解决的上述两个场景)。 这将是下面类似的东西。

BooleanBuilder builder = new BooleanBuilder(predicateBuildFromHttpRequest);
        builder.and(predicateAddressingOurRequirements);
        builder.getValue();

@PostFilter不会成为一个选项,因为所有回购的回复类型为Page<Entity>

我想要解决的用例对我来说似乎很常见。说过我查看了spring-data和spring-data-rest文档,找不到与我的问题有关的任何内容。

问题是:我是否遗漏了一些明显的东西,并且很快获胜了?或者我需要自己实施自定义解决方案?非常感谢任何评论!

2 个答案:

答案 0 :(得分:1)

Querydsl谓词是由QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver构建的,遗憾的是包装私密,无法直接扩展。

但是,你可以复制一下,添加你的安全谓词逻辑,然后放入你的实现,而不是前一个解析器。

public class MyQueryDslRootResourceArgumentResolver extends RootResourceInformationHandlerMethodArgumentResolver {

    // the most of the code is ommitted, the content is identical with 
    // QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver, 
    // the important part is postProcessMethod where you can modify the predicate


    @Override
    @SuppressWarnings({"unchecked"})
    protected RepositoryInvoker postProcess(MethodParameter parameter, RepositoryInvoker invoker,
                                            Class<?> domainType, Map<String, String[]> parameters) {

        Object repository = repositories.getRepositoryFor(domainType);

        if (!QueryDslPredicateExecutor.class.isInstance(repository)
                || !parameter.hasParameterAnnotation(QuerydslPredicate.class)) {
            return invoker;
        }

        ClassTypeInformation<?> type = ClassTypeInformation.from(domainType);

        QuerydslBindings bindings = factory.createBindingsFor(null, type);

        // modify your predicate here
        Predicate predicate = predicateBuilder.getPredicate(type, toMultiValueMap(parameters), bindings);

        return new QuerydslRepositoryInvokerAdapter(invoker, (QueryDslPredicateExecutor<Object>) repository, predicate);
    }


}

然后使用自定义解析程序实现添加自己的配置类。

public class CustomRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {

    @Autowired
    ApplicationContext applicationContext;

    @Override
    public RootResourceInformationHandlerMethodArgumentResolver repoRequestArgumentResolver() {
        QuerydslBindingsFactory factory = applicationContext.getBean(QuerydslBindingsFactory.class);
        QuerydslPredicateBuilder predicateBuilder = new QuerydslPredicateBuilder(defaultConversionService(),
                factory.getEntityPathResolver());

        return new MyQueryDslRootResourceArgumentResolver(repositories(),
                repositoryInvokerFactory(defaultConversionService()), resourceMetadataHandlerMethodArgumentResolver(),
                predicateBuilder, factory);
    }
}

答案 1 :(得分:0)

这是一个示例项目,在将其传递给 谓词 (由url中的参数生成) >存储库

David Siro在上面解释的内容的演示

https://github.com/yeldarxman/QueryDslPredicateModifier