gpt4 book ai didi

spring - 如何在 Spring-Data-Rest 中实现细粒度的访问控制?

转载 作者:行者123 更新时间:2023-12-04 12:40:52 25 4
gpt4 key购买 nike

TL;DR:如何在 Spring-Data-Rest 提供的扁平化 REST api 方法中实现细粒度的访问控制?

所以 - 我正在使用 Spring-Data-Rest 制作一个 API,其中有三个主要访问级别:

1) 管理员 - 可以查看/更新所有组

2) 组的所有者 - 可以查看/更新组及其下的所有内容

3) 子组的所有者 - 只能查看/更新他的组。没有递归嵌套,只允许一个子级别。

并且“组”作为资源公开(有一个 crud 存储库)。

到目前为止一切顺利 - 我已经使用存储库事件处理程序实现了一些修改访问控制 - 所以在创建/写入/删除方面我认为我很好。

现在我需要达到限制某些项目的可见性的地步。这对于获取单个项目是可以的,因为我可以使用 Pre/Post Authorize 注释并引用主体。

问题在于 findAll() 方法——我没有一个简单的钩子(Hook)来根据当前主体过滤掉我不想暴露的特定实例。例如 - 子组所有者可以通过执行 GET/groups 查看所有组。理想情况下,他们应该让他们无法访问的项目甚至根本不可见。

对我来说,这听起来像是在存储库接口(interface)上编写自定义 @Query() 注释,但这似乎不可行,因为:

  • 我需要在查询中引用主体。应该支持 SPeL,但似乎根本不适用于 ?# 表达式(尽管这篇博客文章另有建议:https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions)。我通常使用带有 1.1.8.RELEASE 的 spring-boot 和用于 spring-data 的 Evans-RELEASE 训练。
  • 我需要编写的查询类型将根据访问级别而有所不同,这实际上不能包含在单个 JPQL 语句中(如果管理员选择所有组,否则获取与主体用户关联的所有(子)组)。

  • 因此,听起来我需要为此编写一些自定义存储库实现,并在代码中引用主体。没关系 - 但对于我需要控制访问的每个存储库来说,似乎需要做很多工作(我认为这几乎是所有存储库)。这适用于 findAll 和各种自定义搜索方法。

    我接近这个错误吗?是否有另一种方法可以更好地根据当前登录的用户动态限制项目可见性?在像 spring-data-rest 暴露这样的平面命名空间中,我想这将是一个常见问题。

    在之前的设计中,我只是通过暴露/api/groups/{groupId}/... 下的所有内容来解决它,并让子资源定位器充当一个单一的夹点来控制对其下任何内容的访问。在 spring-data-rest 中没有这样的运气。

    更新:现在绊倒了一个覆盖 findAll() 的自定义方法(这适用于我的自定义界面上定义的其他方法)。虽然这可能是一个单独的问题 - 我现在被阻止了。当我执行 GET/groups 时,Spring-data 只是不调用它,而是调用原始数据。奇怪的是,如果我在接口(interface)上定义一个查询并用@Query 标记它(也许不再支持内置方法的自定义覆盖?),它确实会使用我的查询。
    public interface GroupRepository extends JpaRepository<Group, Long>, GroupCustomRepository {}


    public interface GroupCustomRepository {

    Page<Group> findAll(Pageable pageable);

    }

    public class GroupCustomRepositoryImpl extends SimpleJpaRepository<Group, Long> implements GroupCustomRepository {

    @Inject
    public GroupCustomRepositoryImpl(EntityManager em) {
    super(Group.class, em);
    }

    @Override
    public Page<Group> findAll(Pageable pageable) {

    MyPrincipal principal = (MyPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

    Page<Group> result;
    if (principal.isAdmin()) {
    result = findAll(pageable);
    } else {

    Specification<Group> spec = (root, query, cb) -> cb.or(
    cb.equal(root, principal.getGroup()),
    cb.and(cb.isNotNull(root.get(Group_.parentGroup)), cb.equal(root.get(Group_.parentGroup), principal.getGroup()))
    );

    result = findAll(spec, pageable);
    }

    return result;
    }
    }

    更新 2:由于我无法访问 @Query 中的主体,也无法使用自定义方法覆盖它,因此我陷入了困境。 @PostFilter 也不起作用,因为返回对象是 Page 而不是集合。

    我决定仅将/groups 隔离给管理员,并让其他所有人使用 @PostFilters/@PostAuthorizations 的不同方法(/groups/search/somethingSpecific)。

    不过,这似乎与 HAL 方法不太吻合。对其他人如何使用 Spring-data-rest 解决此类问题感兴趣。

    最佳答案

    我们最终按以下方式解决了这个问题:

  • 我们创建了一个自定义方面,它位于存储库中 CRUD 方法的前面。然后它查找并调用关联的“授权处理程序”,该处理程序在动态管理授权详细信息的存储库上进行注释。
  • 在限制 findAll() 查询中的结果(例如:查看/users)时,我们不得不非常严厉——本质上,只有管理员可以列出所有敏感信息。否则,受限用户必须对特定项目使用查询方法。
  • 我们创建了一些可重用的授权相关类,并在某些场景中使用它们——特别是自定义查询,例如:

    @PreAuthorize("@authorizations.systemAdminRead()")
    @Query("select u FROM User r where ...")
    列出 findAll();

    @PostAuthorize("@otherAuthorizationHandler.readAllowed(returnObject)") ResponseObject someQuery();

  • 总而言之,它有效——但感觉很笨拙,而且很容易错过一些东西。我确实希望这更多地融入到框架中,即使能够动态调整默认查询也会很有用(当我尝试这样做时,我无法使用@Query 适本地更新查询)。

    我们碰巧使用的是 PostgreSQL,因此即将推出的行级安全性 ( http://michael.otacoo.com/postgresql-2/postgres-9-5-feature-highlight-row-level-security/) 将非常符合要求,假设我们可以通过数据库连接为其提供适当的授权详细信息。

    关于spring - 如何在 Spring-Data-Rest 中实现细粒度的访问控制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26643036/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com