【问题标题】:Security Context in terms of QueryDslPredicateExecutor and Spring Data RestQueryDslPredicateExecutor 和 Spring Data Rest 方面的安全上下文
【发布时间】:2017-01-26 09:36:46
【问题描述】:

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

存储库中公开的几乎所有findAll 方法都应该解决两种情况

  • principal 有一个角色ROLE_ADMIN,则不应对来自Pageable,Sort 的部分应用过滤

  • 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 {    

我想调整那个谓词(我想解决上面的 2 个场景)。 这将与下面类似。

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

@PostFilter 不是一个选项,因为所有 repos 的返回类型都是 Page&lt;Entity&gt;

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

问题是:我是否在这里遗漏了一些明显的东西并且可以快速获胜?还是我需要自己实施自定义解决方案?任何 cmets 都非常感谢!

【问题讨论】:

  • 见这里有关自定义用户指定参数的绑定的一些信息。不确定您是否还可以添加其他参数但可能有用:stackoverflow.com/questions/40384132/…
  • 除了上述方法之外,还有一种方法是通过 Servlet 过滤器实际修改传入请求并附加额外的 URL 参数。
  • 您将无法在 QuerydslBinderCustomizer 中公开新参数。它仅用于自定义默认绑定器行为,即忽略大小写、自定义比较等...
  • 自定义过滤器方法是那时我能看到的唯一方法。我在类似的场景中使用过它,它可以工作,但感觉有点 hacky。

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


【解决方案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);
    }
}

【讨论】:

  • 感谢这种方法 - 它有效!但是,当我在我的项目中添加RepositoryRestMvcConfiguration 的扩展名时,Spring Data REST 的“标准”配置过程停止按预期工作,即如果我将 SDR 设置添加到“application.properties”它们不起作用——我能做的就是正在通过RepositoryRestConfigurerAdapter 启用它们。例如,如果我想启用“枚举翻译”,我需要将其设置为 in code,而不是仅在属性文件中设置它,例如“spring.data.rest.enable-enum-translation=true”。
  • 此外,标准转换器也停止工作。我需要在配置类中手动添加它们:objectMapper.registerModule(new JavaTimeModule());。你有没有遇到过这样的问题?你知道怎么解决吗? IE。返回 SDR 的自动配置返回...
【解决方案2】:

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

David Siro 上面解释的演示

https://github.com/yeldarxman/QueryDslPredicateModifier

【讨论】:

    猜你喜欢
    • 2015-05-01
    • 2017-08-26
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 2015-03-27
    • 2015-09-13
    • 2014-12-29
    • 2014-03-05
    相关资源
    最近更新 更多