信息软件中一个非常重要的问题是具有不同职责和访问级别的不同角色的用户的存在。例如,想象一下具有如下结构(层次结构)的组织:
[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'级别,因此他只能看到'人员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,并在每个读取方法之前执行'enablefilter'命令。
@Filters( {
@Filter(name=“EmployeeAuthorize", condition="(organization_id in (select uo.organization_id from user_organization uo where uo.user_id=:authenticatedUserId) ) ")
} )
现在的问题是,建议的解决方案是否合适?如果是,如何在弹簧数据中使用此方法? PS:鉴于我们不想依赖数据库,数据库端的实现不能成为候选解决方案,因此我们不得不在应用程序端(级别)实现它。
答案 0 :(得分:4)
阿里,这是一个有趣的场景。
您需要在此处回答两个问题。
第一个问题 - 在公开数据时,系统是要进行过滤还是超出此范围? 例如,如果您公开类似 users / {id} 的操作,那么您需要检查授权 - 并确保用户可以访问该操作。如果您只是公开像 / users 这样的操作 - 那么您只需要过滤,因为您只需公开当前用户有权查看的用户。 这种区别将决定许多实施。
第二个问题是 - 您可以做多少手工工作?
一方面,您可以根据框架需要调整数据 - 并尝试尽可能多地依赖内置功能(安全表达式,ACL)。 或者,另一方面,您可以使代码适应数据结构 - 并且更加手动地执行操作。
这是我首先关注的两个因素,首先是因为基于这4个决定,实施将完全不同。
最后,回答你的“可以ACL规模”问题 - 两个快速说明。 一个 - 你需要测试。是的,ACL可以扩展,但它可以扩展到10K或100K不是一个可以具体回答的问题,无需测试。
第二,当你做测试时,要考虑现实场景。理解解决方案的局限性当然很重要。但是,除此之外,如果您认为您的系统将拥有1M实体 - 非常棒。但如果不这样做 - 那就不要成为一个目标。
希望有所帮助。
答案 1 :(得分:3)
使用Spring,您可以使用以下内容:
1)您可以使用SpEL EvaluationContext extension使@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)您可以在网络安全表达式中refer to Beans并使用path variables。这允许您仅授予对当前用户允许的那些对象的访问权限:
@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)")
// ...
}
}
查看my demo project了解详情。在此示例中,如果客户属于与这些用户相关的类别,则他们可以访问客房。管理员可以访问所有房间。