我读到有关DDD和访问控制的内容,我发现以下两种观点之间存在一些矛盾:
我正在寻找关于此的最佳实践。那么我应该在哪里通过域驱动设计放置访问控制逻辑,我应该如何实现它呢?
(更具体地说,DDD + CQRS + ES。)
我认为它应该接近业务逻辑,例如用户故事可能是这样的:
用户可以通过发送用户名,爱好列表,简历等来编辑他的个人资料......
根据用户故事,我们实施域模型和服务,例如:
UserService
editProfile(EditUserProfileCommand command)
User user = userRepository.getOneById(command.id)
user.changeName(command.name)
user.changeHobbies(command.hobbies)
user.changeCV(command.cv)
UserRepository
User getOneById(id)
User
changeName(String name)
changeHobbies(String[] hobbies)
changeCV(String cv)
这没关系,但故事的HIS profile
部分在哪里?
这显然是基于属性的访问控制,因为我们应该编写如下规则:
deny all, but if subject.id = resource.owner.id then grant access
但是我们应该在哪里执行这条规则,我们应该如何实施呢?
答案 0 :(得分:36)
那么我应该把访问控制逻辑放在哪里呢?
根据这一点:https://softwareengineering.stackexchange.com/a/71883/65755策略执行点应该在调用UserService.editProfile()
之前。
我得出了相同的结论:它不能在UI中,因为通过多个UI我们会重复代码。它应该在创建域事件之前,因为它们表明我们已经在系统中做了一些事情。因此,我们可以限制对域对象或使用这些域对象的服务的访问。通过CQRS,我们不需要读取模型的域对象,只需要服务,因此如果我们需要通用解决方案,我们必须限制对服务的访问。我们可以将访问决策放在每个服务操作的开头,但这将是grant all, deny x
安全反模式。
我该如何实施?
这取决于哪个访问控制模型适合域,因此它取决于用户故事。通过访问决策,我们通常会发送访问请求并等待权限作为回报。访问请求通常包含以下部分:主题,资源,操作,环境。因此,主题需要许可才能对环境中的资源执行操作。首先,我们确定主题,然后我们对其进行身份验证,然后在授权之后,我们检查访问请求是否适合我们的访问策略。每个访问控制模型都以类似的方式工作。 OFC。他们可能缺少一些这些步骤,但这无关紧要......
我创建了一个访问控制模型的简短列表。我将规则,策略放入注释中,但通常我们应该将它们存储在可能采用XACML格式的数据库中,如果我们想要一个维护良好的系统...
通过基于身份的访问控制(IBAC),我们拥有身份 - 权限存储(访问控制列表,功能列表,访问控制矩阵)。因此,例如通过访问控制列表,我们存储可以具有权限的用户或组的列表。
UserService
@AccessControlList[inf3rno]
editProfile(EditUserProfileCommand command)
通过基于格的访问控制(LBAC),主体具有许可级别,资源具有所需的许可级别,并且我们检查哪个级别更高......
@posseses[level=5]
inf3rno
UserService
@requires(level>=3)
editProfile(EditUserProfileCommand command)
通过基于角色的访问控制(RBAC),我们定义了主题角色,并且我们授予对其行为起实际作用的主体的权限。
@roles[admin]
inf3rno
UserService
@requires(role=admin)
editProfile(EditUserProfileCommand command)
通过基于属性的访问控制(ABAC),我们定义主题,资源和环境属性,并根据它们编写策略。
@attributes[roles=[admin]]
inf3rno
UserService
@policy(subject.role=admin or resource.owner.id = subject.id)
editProfile(EditUserProfileCommand command)
@attribute(owner)
Subject getOwner(EditUserProfileCommand command)
通过基于策略的访问控制(PBAC),我们不会将我们的策略分配给其他任何策略,它们是独立的。
@attributes[roles=[admin]]
inf3rno
UserService
editProfile(EditUserProfileCommand command)
deleteProfile(DeleteUserProfileCommand command)
@attribute(owner)
Subject getOwner(EditUserProfileCommand command)
@permission(UserService.editProfile, UserService.deleteProfile)
@criteria(subject.role=admin or resource.owner.id = subject.id)
WriteUserServicePolicy
通过风险自适应访问控制(RAdAC),我们根据受试者的相对风险状况和操作的风险等级做出决策。我认为这不能用规则来描述。我不确定实现,也许这就是stackoverflow它的点系统所使用的。
通过基于授权的访问控制(ZBAC),我们不进行身份验证和身份验证,而是为身份识别因素分配权限。例如,如果有人发送令牌,那么她可以访问服务。其他一切与之前的解决方案类似。例如,使用ABAC:
@attributes[roles=[editor]]
token:2683fraicfv8a2zuisbkcaac
ArticleService
@policy(subject.role=editor)
editArticle(EditArticleCommand command)
所以知道2683fraicfv8a2zuisbkcaac
令牌的每个人都可以使用该服务。
依旧......
还有许多其他型号,最适合的总是取决于客户的需求。
总结
- "security concerns should be handled outside the domain"
- "access control requirements are domain specific"
两者都可以,因为安全性不是域模型的一部分,但它的实现取决于域模型和应用程序逻辑。
2年后修改 2016年9月5日
由于我作为DDD新手回答了我自己的问题,我已经阅读了Vaughn Vernon的Implementing Domain-Driven Design。这是一本有趣的书。以下是它的引用:
这构成了一个新的有界上下文 - 身份和访问 上下文 - 并将通过标准由其他有界上下文使用 DDD集成技术。对于消费环境而言,身份和 Access Context是一个通用子域。该产品将被命名 IdOvation。
因此根据Vernon可能是将访问控制移动到通用子域的最佳解决方案。