Spring Data JPA和spring-security:在数据库级别上过滤(特别是对于分页)

时间:2013-02-27 09:32:30

标签: spring-security spring-data querydsl

我正在尝试使用注释和spring-security为我的开源项目添加方法级安全性。我现在面临的问题是findAll方法,尤其是Paging的方法(例如,返回页面)。

使用@PostFilter可以在列表中使用(但我个人认为在应用程序而不是数据库中进行过滤不是一个好主意),但在分页查询时完全失败。

这是有问题的,因为我有一个包含List<Compound>的实体。复合有不同的实现,用户可能只有权读取其中一个化合物。 Compound使用TABLE_PER_CLASS继承。存储库实现QueryDslPredicateExecutor

我的想法是为每个查询添加一个谓词,根据当前用户限制返回结果。然而,我有点迷失在a)用户和角色的数据模型应该如何看,以及b)如何创建谓词(一旦定义了模型,这可能很容易)。或者querydsl是否已经提供基于类型的过滤(在查询类中包含的元素上)?

2 个答案:

答案 0 :(得分:7)

目前没有这样的支持,但我们在路线图上有这样的支持。您可能希望关注DATACMNS-293以获得一般进展。

答案 1 :(得分:2)

暂时提出以下解决方案。由于我的项目相当简单,因此可能无法用于更复杂的项目。

  1. 用户可以读取某个类的所有实体
  2. 因此,任何查询方法都可以使用包含@PreAuthorize的{​​{1}}进行注释。

    这个例外是我项目中的hasRole实体。它可以包含Container的任何子类,并且用户可能没有权限查看所有子类。它们必须是过滤器。

    为此,我创建了一个CompoundUser实体。 RoleCompound具有OneToOne关系,该角色是Role的“read_role”。 CompoundUser具有ManyToMany关系。

    Role

    我的所有存储库都实现了@Entity public abstract class Compound { //... @OneToOne private Role readRole; //... } ,这就变得非常有用了。我们不是在存储库中创建自定义findBy方法,而是仅在服务层中创建它们,并使用QueryDSLPredicateExecutorrepositry.findAll(predicate)。谓词保存实际用户输入+“安全过滤器”。

    repository.findOne(predicate)

    注意:@PreAuthorize("hasRole('read_Container'") public T getById(Long id) { Predicate predicate = QCompoundContainer.compoundContainer.id.eq(id); predicate = addSecurityFilter(predicate); T container = getRepository().findOne(predicate); return container; } private Predicate addSecurityFilter(Predicate predicate){ String userName = SecurityContextHolder.getContext().getAuthentication().getName(); predicate = QCompoundContainer.compoundContainer.compound.readRole .users.any().username.eq(userName).and(predicate); return predicate; } 是QueryDSL生成的“元模型”类。

    最后,您可能需要初始化从QCompoundContainerContainer的QueryDSL路径:

    User

    省略最后一步可能会导致@Entity public abstract class CompoundContainer<T extends Compound> //... @QueryInit("readRole.users") // INITIALIZE QUERY PATH @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity=Compound.class) private T compound; //... }

    进一步提示:NullPointerException自动设置保存角色:

    CompoundService

    这很有效。缺点是有点明显。相同的if (compound.getReadRole() == null) { Role role = roleRepository.findByRoleName("read_" + getCompoundClassSimpleName()); if (role == null) { role = new Role("read_" + getCompoundClassSimpleName()); role = roleRepository.save(role); } compound.setReadRole(role); } compound = getRepository().save(compound) 与同一Role类实现的每个实例相关联。