如何在Spring Data JPA中使用Hibernate过滤器或其他方法实现行级安全性?

15
在信息软件中非常重要的问题之一是存在具有不同职责和访问级别的不同角色的用户。例如,考虑以下结构(层次结构)的组织:
[Organization Role ]     [Organization ID]
 CEO                        org01
   Financial Assistant      org0101
           personnel 1

   Software Assistant       org0102
           personnel 2

   Commercial Assistant     org0103
           personnel 3

想象一下,这个组织有一个管理人员信息的系统。在这个系统中,显示人员信息的规则是每个用户只能看到他所拥有访问权限的组织的人员信息;例如,“user1”可以访问“财务助理”和“商业助理”级别,因此他只能看到“personnel 1”和“personnel 3”的信息。同样,“user2”只能访问“商业助理”级别,因此他只能看到“personnel 3”的信息。因此,该系统中的每个用户都有特定的访问级别。 现在考虑,在该系统中,每个用户登录后只能看到他所拥有访问权限的人员信息。假设该系统的表结构如下:
[Organization]
id
code
name

[Employee]
id
first_name
last_name
organization_id

[User]
id
user_name
password

[UserOrganization]
user_id
organization_id

以下查询足以获取每个用户的正确人员信息结果:

select *

from employee e 

where e.organization_id in

(select uo.organization_id

 from user_organization uo

 where uo.user_id=:authenticatedUserId)

正如我们所看到的,下面的条件定义了显示正确数据的访问逻辑:

e.organization_id in

(select uo.organization_id

 from user_organization uo

 where uo.user_id=:authenticatedUserId)

这种访问级别也被称为“行级安全”(RLS)。 另一方面,相应的存储库类可能有几个负责读取数据的方法,所有这些方法都必须满足适当的访问级别条件。在这种情况下,访问级别条件将在某些地方(方法)重复出现。似乎使用“Hibernate过滤器”是解决此问题的适当方法。唯一需要的是一个过滤器,它获取经过身份验证的用户的ID,并在每个读取方法之前执行“enablefilter”命令。

@Filters( {
  @Filter(name=“EmployeeAuthorize", condition="(organization_id in (select uo.organization_id from user_organization uo where uo.user_id=:authenticatedUserId) )  ")
} )

现在的问题是,所提出的解决方案是否正确?如果是,如何在Spring Data中利用这种方法? 附注:鉴于我们不想依赖数据库,因此在数据库端实现不能成为候选解决方案,出于这个原因,我们被迫在应用程序端(层)实现它。

假设组织节点数为100,000,想要让管理员用户访问所有组织?如何使用ACL实现? 当在组织中创建新节点时,我将添加对所有用户的访问权限。这并不具有良好的性能,想象一下我们有10,000个用户,访问表中会保存多少记录? - ali akbar azizkhani
2个回答

6

阿里,这是一个有趣的场景。

你需要回答两个问题

第一个问题 - 当暴露数据时,系统只会进行过滤还是会超越过滤? 例如,如果您公开像users/{id}这样的操作,则需要检查授权并确保用户可以访问该操作。如果您只公开像/users这样的操作,则只需要过滤,因为您将仅公开当前用户有权查看的用户。 这种区别将决定很多实现方式。


第二个问题是 - 你可以接受多少手动工作?

一方面,您可以使数据适应框架所需的内容,并尽可能依赖内置功能(安全表达式、ACL)。 或者,另一方面,您可以使代码适应数据结构 - 并且更多地手动处理。

这些是我首先关注的两个因素,在任何其他事情之前 - 因为基于这些4个决策,实现将完全不同。


最后,回答您的“ACL是否可扩展”的问题 - 两个快速说明。 一 - 您需要测试。是的,ACL可以扩展,但它是否能够扩展到10K或100K并不是一个可以具体回答的问题,需要测试。

其次,在测试时,请考虑实际情况。了解您解决方案的限制当然很重要。但是,除此之外,如果您认为您的系统将有100万个实体 - 那太好了。但如果不会 - 那就不要把它作为目标。

希望这能帮到您。


1
基于一个单一的查找会在4个表上产生一个连接,我怀疑默认实现在这种情况下不会很好地扩展(虽然我也可能错了)。如果不需要保持数据在第三范式中,你也可以使用MongoDB方法,它只为每个域对象创建一个条目,因此应该可以线性扩展。不幸的是,我没有从Spring Security社区得到任何真正的回应,他们是否有兴趣将基于MongoDB的ACL集成到他们的代码库中。 - Roman Vottner
嘿@RomanVottner - 关于MongoDB和ACL的快速说明。一般来说,在Spring生态系统中,我看到最好的解决方案是社区解决方案,最终被官方采纳和集成。如果您有兴趣参与其中,最好的方法是推出这个解决方案。它有可能最终成为官方版本,但即使不是,它仍然是社区可以使用的东西。 - Eugen
1
谢谢,是的,我想授权用户进行保存和加载。在保存中,如果没有ACL或表达式,我无法处理它。但在加载对象时,Hibernate过滤器可以正常工作。但是,在这种情况下使用ACL时,我的问题在于更改用户组织访问权限并添加和删除ACL_ENTRY。 - ali akbar azizkhani
当一个组织的数量达到100,000,而我有200,000个用户时,ACL_ENTRY的计数是多少?是100,000*200,000吗?我该如何处理呢? - ali akbar azizkhani
@aliakbarazizkhani 取决于您每个ACL(~=组织)定义了多少实际规则。用户将是ACL设置中的“SID”,而组织将是ACL指向的“ObjectIdentity”的,而“AccessControlEntry”将为给定域对象上的特定用户定义实际权限。因此,除非您单独为每个用户定义规则,否则计数应该要少得多。尽管在过滤开始之前,联接表可能仍会累积大量数据(但我不是SQL专家)。还请注意,权限是可继承的。 - Roman Vottner
@Eugen 我看到ACL代码(JdbcMutableAclService.java),当ACL更新时,所有条目都会被删除并重新创建。如果我有200,000个条目,当我想要添加新条目时会发生什么?这样可以吗? - ali akbar azizkhani

4

使用Spring,您可以使用以下功能:

1)您可以使用SpEL EvaluationContext扩展,将安全属性和表达式在@Query注释中的SpEL表达式中进行使用。这使您仅获取与当前用户相关的业务对象:

interface SecureBusinessObjectRepository extends Repository<BusinessObject, Long> {

    @Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
    List<BusinessObject> findBusinessObjectsForCurrentUser();
}

2) 您可以查看Beans并在Web安全表达式中使用路径变量。这使您能够仅授予当前用户允许的对象的访问权限:

@Service
public class UserService {
    public boolean checkAccess(Authentication authentication, int id) {
        // ...
    }
}

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/businessObjects/{id}/**").access("@userService.checkAccess(authentication, #id)")
            // ...
    }
}

检查我的演示项目获取更多详细信息。在这个例子中,如果用户属于和这些用户相关的类别,则用户可以访问房间。管理员可以访问所有房间。

1
当您的类别计数达到1,000,000时,您的解决方案是否有效? - ali akbar azizkhani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接