11

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

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

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

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

4

3 回答 3

7

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

于 2013-02-28T09:19:16.500 回答
2

暂时想出了以下解决方案。由于我的项目相当简单,这可能不适用于更复杂的项目。

  1. 用户可以读取某个类的所有实体或不读取任何实体

因此任何查询方法都可以用@PreAuthorize包含注释hasRole

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

为此,我创建了一个UserandRole实体。Compound具有 OneToOne 关系,Role并且该角色是 that 的“read_role” CompoundUserRole具有多对多关系。

@Entity
public abstract class Compound {    
    //...
    @OneToOne    
    private Role readRole;
    //...   
}

我所有的存储库都实现QueryDSLPredicateExecutor了,这在这里变得非常有用。repositry.findAll(predicate)我们只在服务层创建它们并使用and ,而不是在存储库中创建自定义 findBy-methods 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;
}

注意:QCompoundContainer是 QueryDSL 生成的“元模型”类。

最后,您可能需要将 QueryDSL 路径初始化ContainerUser

@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的每个实例相关联。Compound

于 2013-02-28T13:36:15.787 回答
0

8 年后,仍然没有开箱即用的解决方案。因此,我创建了支持将安全条件注入所有 JPA 存储库操作的库,包括:

  • 基本操作 ( findOne, findAll, save, delete)
  • 具有规范 ( findAll(Specification, ...))的查找器
  • querydsl finders(与 Spring WEB 和 Spring Data REST 一起使用)
  • 用户特定的查找器 ( findByValue)

https://github.com/vlsergey/spring-data-entity-security

于 2021-08-25T17:20:24.447 回答