13

信息软件中非常重要的问题之一是存在具有不同职责和访问级别的不同角色的用户。例如,考虑一个具有如下结构(层次结构)的组织:

[Organization Role ]     [Organization ID]
 CEO                        org01
   Financial Assistant      org0101
           personnel 1

   Software Assistant       org0102
           personnel 2

   Commercial Assistant     org0103
           personnel 3

想象一下,这个组织有一个管理人员信息的系统。本系统显示人员信息的规则是每个用户都可以看到他所访问的组织的人员信息;例如,“user1”可以访问“财务助理”和“商务助理”级别,所以他只能看到“人员 1”和“人员 3”的信息。同样,'user2'只有'Commercial Assistant'级别的权限,所以他只能看到'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)。另一方面,相应的存储库类可能有几个负责读取数据的方法,所有这些方法都必须满足适当的访问级别条件。在这种情况下,访问级别条件将在某些地方(方法)重复。似乎使用“休眠过滤器”将是解决此问题的正确方法。唯一需要的是一个过滤器,它获取经过身份验证的用户的 id 并在每个读取方法之前执行“启用过滤器”命令。

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

现在的问题是,建议的解决方案是否正确?如果是,如何在spring数据中使用这种方法?PS:鉴于我们不想依赖数据库,数据库端的实现不可能是一个候选的解决方案,因此我们有义务在应用端(级别)实现它。

4

2 回答 2

6

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

您需要在这里回答两个问题。

第一个问题- 在公开数据时,系统是否只是进行过滤,还是您会超出此范围?例如,如果您公开了类似users/{id}的操作- 那么您需要检查授权 - 并确保用户有权访问该操作。如果您只是公开像/users这样的操作- 那么您所需要的只是过滤,因为您只需公开当前用户有权查看的用户。这种区别将决定很多实施。


第二个问题是——你可以做多少体力活?

一方面,您可以根据框架的需要调整数据 - 并尝试尽可能多地依赖内置功能(安全表达式,ACL)。或者,另一方面,您可以使代码适应您的数据结构 - 并且更多地手动执行操作。

这是我首先要关注的两个因素——因为根据这 4 个决定,实施看起来会完全不同。


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

其次,当您进行测试时,请考虑现实场景。了解解决方案的局限性当然很重要。但是,除此之外,如果您认为您的系统将拥有 100 万个实体,那就太好了。但如果它不会——那么就不要把它作为目标。

希望有帮助。

于 2017-10-16T07:50:54.773 回答
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)您可以在Web Security Expressions中引用Beans并使用路径变量。这允许您仅授予当前用户允许的那些对象的访问权限:

@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)")
            // ...
    }
}

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

于 2017-10-15T18:30:22.437 回答