Spring Boot:数据访问取决于角色
Posted
技术标签:
【中文标题】Spring Boot:数据访问取决于角色【英文标题】:Spring Boot: Data access depending on role 【发布时间】:2019-12-25 10:40:34 【问题描述】:对于数据仓库中的 REST-API,我需要一些基于角色的数据访问。首先让我们通过一些小例子来阐明需求。我们定义实体Author
和Book
,它们都使用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;
然后我定义了两个角色User
和Admin
。通常作者是普通用户,所以授权机制只为角色User
添加一个SimpleGrantedAuthority
。但也有一些特殊的作者,另外还有Admin
这个角色。
角色User
的普通作者调用url \books
时,他应该只获得他拥有的书籍,而角色Admin
的作者应该获得所有存在的书籍。同样对于PUT/PATCH/DELETE
请求,具有User
角色的作者应该只能更新/删除他们自己的图书,而Admin
角色可以对所有图书执行此操作。
我的问题:有没有办法在Controller
类中定义数据访问一次?我从Django-Framework
知道类似的东西,在那里我可以覆盖方法get_queryset()
,它为每个“视图”方法(GET/LIST/CREATE/UPDATE/etc.)提供了可使用的数据集。我目前存档的方式是在控制器中为不同的 API 端点定义方法,而不是管理那里的访问。这导致了两个问题:
我认为这应该是一个常见的问题,但我还没有找到一个通用的解决方案。所以我很感谢每一个建议。
编辑:“安全方法”示例
@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-REST 调用中获取用户信息?
Spring Boot Security 基于角色的访问控制