当前位置: 首页 > 知识库问答 >
问题:

Spring Data JPA和sping-Security:数据库级别的过滤器(尤其是分页)

丁鸿信
2023-03-14

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

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

这是有问题的,因为我有一个包含List的实体

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


共有3个答案

庾波光
2023-03-14

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

  • 基本操作(findOnefindAll保存、<代码>删除
  • 具有规范的查找器(findAll(规范,…)
  • querydsl finders(用于Spring WEB和Spring Data REST)
  • 特定于用户的查找器(findByValue

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

范稳
2023-03-14
匿名用户

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

    用户可以读取某个类
  1. 的所有实体,也可以不读取某个类的实体

因此,任何查询方法都可以用包含< code>hasRole的< code>@PreAuthorize进行注释。

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

为此,我创建了User角色实体CompoundRoleOneToOne关系,该角色是该Compound的“read_Role”用户角色具有多对多关系。

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

我的所有存储库都实现了QueryDSLPredicateExec的,这在这里变得非常简单。我们不是在存储库中创建自定义findBy-方法,而是仅在服务层创建它们,并使用repositry.findAll(谓词)repository.findOne(谓词)。谓词保存实际用户输入的“安全过滤器”。

@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路径从Container初始化为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)

这个管用。缺点有点明显。同一个< code >角色与同一个< code >复合类实现的每个实例相关联。

田志
2023-03-14

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

 类似资料:
  • 操作符(operator) 用来改变WHERE子句中的子句关键字,也成逻辑操作符。 AND操作符 通过使用AND来给WHERE子句附加条件。 索引出供应商'DLL01'制造且价格小于等于4美金的所有产品名称和价格。 mysql> SELECT prod_id, prod_price, prod_name -> FROM Products -> WHERE vend_id = 'D

  • 问题内容: 我应该使用哪个过滤器来定义要使用apender记录的特定级别?例如: Java的 log4j2.xml: 文件输出很好,但是在控制台中,我得到了这样的结果: 但是我需要追加程序仅输出INFO消息,而输出所有EXEPT INFO。因此,控制台输出应如下所示: 无法找到如何防止过滤器尊重级别继承的方法。是否有可能做到这一点? 问题答案: 这有效:

  • 我们提供了一个数据过滤器来对接收到的表单数据进行过滤。整个数据过滤分四步: 非空验证 数据类型验证 数据长度验证 数据净化 过滤器定义了一些验证规则的常量,供你组合使用,采用位运算的形式,如果要同时验证多个指标,请使用与运算(|), 例如: DFILTER_STRING|DFILTER_SANITIZE_TRIM. 表示数据必须是字符串并对字符串进行去空格操作。 数据类型验证选项值 选项名称 选项

  • 问题内容: 我们添加到我们现有的项目中。 从这一刻起,我们从服务器收到401 错误。 这是因为没有标头附加到响应。为了解决这个问题,我们Filter在退出过滤器之前的链中添加了我们自己的过滤器,但是该过滤器不适用于我们的请求。 我们的错误: XMLHttpRequest无法加载。所请求的资源上不存在“ Access-Control-Allow-Origin”标头。http://localhost:

  • 这是因为响应中没有附加头。为了解决这个问题,我们添加了自己的筛选器,它位于注销筛选器之前的链中,但该筛选器不适用于我们的请求。 我们的错误: XMLHttpRequest无法加载。请求的资源上没有“access-control-allow-origin”标头。因此,不允许访问Origin。响应的HTTP状态代码为401。 我们的过滤器 我们的申请 我们的筛选器是从Spring-Boot注册的: 尝

  • 问题内容: 谁能告诉我SQL Server中数据库级别触发器和服务器级别触发器之间的区别吗? 提前致谢。 问题答案: SQL Server2005中引入了可以被设置成火你所选择的DDL事件,比如DML触发器,,,,等。 DDL触发器可以在2个范围内设置: 服务器范围 :使用服务器范围创建的触发器必须以服务器DDL事件为目标,例如CREATE_DATABASE或CREATE_LOGIN 数据库范围