Spring Boot:数据访问取决于角色

Posted

技术标签:

【中文标题】Spring Boot:数据访问取决于角色【英文标题】:Spring Boot: Data access depending on role 【发布时间】:2019-12-25 10:40:34 【问题描述】:

对于数据仓库中的 REST-API,我需要一些基于角色的数据访问。首先让我们通过一些小例子来阐明需求。我们定义实体AuthorBook,它们都使用PagingAndSortingRepository 作为它们的默认行为。一个作者可以“拥有”几本书,而一本书只能依赖于一个作者。

简化的实体应如下所示:

@Entity
@Table(name = "Author")
public class Author
    // [..]
    @OneToMany(mappedBy = "author")
    private List<Book> books;


@Entity
@Table(name = "Book")
public class Book
    // [..]
    @ManyToOne
    @JoinColumn(name = "Author_ID")
    private Author author;

然后我定义了两个角色UserAdmin。通常作者是普通用户,所以授权机制只为角色User添加一个SimpleGrantedAuthority。但也有一些特殊的作者,另外还有Admin这个角色。

角色User 的普通作者调用url \books 时,他应该只获得他拥有的书籍,而角色Admin 的作者应该获得所有存在的书籍。同样对于PUT/PATCH/DELETE 请求,具有User 角色的作者应该只能更新/删除他们自己的图书,而Admin 角色可以对所有图书执行此操作。

我的问题:有没有办法在Controller 类中定义数据访问一次?我从Django-Framework 知道类似的东西,在那里我可以覆盖方法get_queryset(),它为每个“视图”方法(GET/LIST/CREATE/UPDATE/etc.)提供了可使用的数据集。我目前存档的方式是在控制器中为不同的 API 端点定义方法,而不是管理那里的访问。这导致了两个问题:

大量工作,实现控制器中的方法 如果您的实体之间有很多依赖关系,我的 dwh 就是这种情况,您可以很容易错过一些端点。因此,我可能有一个端点,每个作者都有完全访问权限,无论是哪个角色。

我认为这应该是一个常见的问题,但我还没有找到一个通用的解决方案。所以我很感谢每一个建议。


编辑:“安全方法”示例

    @RequestMapping(value = "/dimensionAttributeValues", method = RequestMethod.GET)
    @ResponseBody
    public PagedResources<DimensionAttributeValue> getDimensionAttributeValues(Pageable pageable, PersistentEntityResourceAssembler persistentEntityResourceAssembler) 
        Page<DimensionAttributeValue> result;

        if (SecurityUtils.userHasRole(ADMIN) || SecurityUtils.userHasRole(TIMEMANAGER)) 
            result = dimensionAttributeValueService.getAllDimensionAttributeValue(pageable);
         else 
            result = dimensionAttributeValueService.getUserDimensionAttributeValue(SecurityContextHolder.getContext().getAuthentication().getName(), pageable);
        

        PagedResources<DimensionAttributeValue> resources;
        resources = this.toResource(result, persistentEntityResourceAssembler);

        // TODO: Remove dirty Hack!
        Link searchLink = linkTo(DimensionAttributeValueController.class).slash("/dimensionAttributeValues/search").withRel("search");
        resources.add(searchLink);

        return resources;
    

【问题讨论】:

你已经在使用 Spring Security 了吗? 是的,我这样做是为了进行身份验证,并对未经身份验证的用户隐藏所有端点。 您可以在控制器上尝试类似:@PreAuthorize("hasRole('ROLE_ADMIN')") @Sebastian 我已经添加了一些我目前如何做的例子 @Sebastian 仅在不需要根据角色提供不同数据的情况下才有效,请参阅添加的示例。 【参考方案1】:

如果您想在存储库级别执行此操作,spring security 可让您访问存储库中的主体。

无论如何,您都需要为此定义一个自定义查询。

类似于此处描述的内容:第 3.2 章中的https://www.baeldung.com/spring-data-security

否则你可以添加服务层而不是使用@PreAuthorize注解

【讨论】:

如果我正确理解了这个例子,那么我需要为每个端点定义查询。如果必须根据角色提供不同的数据,这很难【参考方案2】:

您可以向存储库调用添加 where 条件,以按角色过滤结果。 因此检查JpaSpecificationExecutor的这个方法:

Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);

示例(现在无法测试,但这应该会引导您朝着正确的方向前进):

import static org.springframework.data.jpa.domain.Specifications.where;
..
Specification<Book> roleFilter = (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("role"), "Admin");

Page<Book> page = this.yourRepository.findAll(where(roleFilter), pageable);

【讨论】:

以上是关于Spring Boot:数据访问取决于角色的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot中基于​​角色的授权

如何在 Spring-Boot-REST 调用中获取用户信息?

Spring Boot Security 基于角色的访问控制

Spring Boot 资源服务器无法使用 oAuth 2 访问令牌授权角色

Spring Boot Security

spring boot集成spring security