实施DDD:用户和权限


16

我正在研究一个小型应用程序,试图掌握域驱动设计的原理。如果成功,这可能是一个更大项目的试验。我正在尝试遵循《实现域驱动的设计》(由Vaughn Vernon撰写)一书,并试图实现一个类似的简单讨论论坛。我还在github上签出了IDDD示例。我在采用身份和案件访问权方面遇到一些困难。让我给出一些背景信息:

  • 我(希望)理解了将用户和权限逻辑分开的原因:这是一个支持域,并且是一个不同的有界上下文。
  • 在核心域中,没有用户,只有作者,主持人等。这些是通过使用服务延伸到“身份和访问”上下文,然后将接收到的User对象转换为and主持人而创建的。
  • 以相关角色作为参数调用域操作:例如:

    ModeratePost( ..., moderator);

  • 域对象的方法检查给定的主持人实例是否不为空(如果从“身份和访问”上下文中询问的用户不具有主持人角色,则主持人实例将为空)。

  • 在一种情况下,它会在更改帖子之前进行另一项检查:

    if (forum.IsModeratedby(moderator))

我的问题是:

  • 在后一种情况下,安全性关注点是否不会再次混入核心域?以前,这些书指出“可以与谁一起发布主题,或者在允许的条件下发布。论坛只需要知道作者正在这样做”。

  • 本书中基于角色的实现非常简单:当主持人是核心域时,它会尝试将当前的userId转换为主持人实例,或在需要时将其转换为作者。服务将以适当的实例进行响应;如果用户没有所需的角色,则该服务将为null。但是,我看不到如何适应更复杂的安全模型。我正在尝试的当前项目具有一个包含组,ACL等的相当复杂的模型。

即使规则不是很复杂,例如:“帖子只能由其所有者或编辑者编辑”,但这种方法似乎无法使用,或者至少我看不到实现该方法的正确方法。

通过向Identity and Access上下文询问OwnerOrEditor实例并不适合,我最终会在核心域中得到越来越多与安全性相关的类。另外,我不仅需要将userId传递给安全上下文,还需要将受保护资源的标识符(帖子,论坛等的ID)传递给安全性上下文,该上下文可能不关心这些事情(对吗? )

通过将权限拉到核心域并在域对象的方法或服务中检查它们,我将得出结论:将安全问题与域混合在一起。

我在某个地方读过(并且我倾向于同意),这些与权限相关的事物不应成为核心域的一部分,除非安全性和权限是核心域本身。像上面给出的那样简单的规则是否足以使安全性成为核心域的一部分?


也许您可以在这里找到所需的内容:stackoverflow.com/a/23485141/329660另外,仅由于访问控制上下文知道资源ID并不意味着它具有有关该资源是什么类型的实体或它是什么的域知识。做。
guillaume31 '18

谢谢,我已经看过较早的帖子了,我的问题恰好是编辑最后的内容:我想将访问控制移出我的核心域,但是我觉得自己的实现遇到了障碍。但是,您对资源ID的建议是有道理的:由于我不在核心域中使用用户或角色的概念,而是具体角色,也许我可以在安全性BC中使用资源的概念并将其映射到相关的具体对象域的概念。值得一试,谢谢!
LittlePilgrim

链接中的代码示例至少不能回答“我看不到如何适应更复杂的安全模型”吗?
guillaume31 '18

我的问题不是安全模型本身的实现,我看不到如何将这些更复杂的规则映射到域中。如果用户->作者映射不是安全方面基于角色的简单模型,应该如何更改?将资源ID传递到其他上下文可能会起作用,例如,HasPermissionToEdit(userId, resourceId)但我觉得用这些调用污染域逻辑是不合适的。在调用域逻辑之前,可能应该在应用程序服务方法中检查这些内容?
LittlePilgrim

当然应该在应用程序服务中……我认为从代码的某些部分(例如UserService @AccessControlList[inf3rno]我链接到的答案)中可以清楚地看出。
guillaume31 '18

Answers:


6

有时很难区分真实的访问控制规则和边界不变,这是访问控制的边界。

特别是,依赖于仅在特定域逻辑过程中才可用的数据的规则可能不容易从域中提取出来。通常,在执行域操作之前或之后(而不是在执行域操作期间)调用访问控制。

沃恩·弗农(Vaughn Vernon)的assert (forum.IsModeratedBy(moderator))示例可能应该在域之外,但并不总是可行的。

我不仅需要将userId传递给安全上下文,还需要将受保护资源的标识符(帖子,论坛等的ID)传递给安全上下文,该上下文可能不关心这些事情(对吗?)

如果有一个安全BC,并且您希望它处理该逻辑,则不必知道论坛的详细信息,而是:

  • 它可能只了解“主持人”之类的概念,并相应地授予或拒绝访问权限。
  • 您可以具有订阅者逻辑,该逻辑订阅Core Domain事件并将其转换为简单的(资源,authorizedUsers)键值对,以供Security BC存储和使用。

由于这两个答案都是有用的,或多或少都指向同一个方向,所以我都对它们都表示赞同。我接受了这个,因为@ guillaume31或多或少地回答了我有关Vernon实施的问题,我将根据他关于在Security BC中使用资源的提示继续我的实施。
LittlePilgrim

我必须说我认为这与我的回答完全相反。
伊万(Ewan)

1
也许我现在有点困惑,但是我的解释是(对于两个答案):1.将安全性问题排除在域之外,并将安全性BC用作服务2.调用任何域对象之前调用服务3.服务将执行从用户/ acl到主持人,作者等的映射moderator = securityService.GetModerator(userId, forumId) 。4.域逻辑将在这些对象中实现,例如在moderator.EditPost()中。5.诸如EditPost之类的方法对安全性概念一无所知,不会进行其他检查那里
LittlePilgrim

我仍在寻找有关此问题的答案/方向,但我发现依赖于对象当前状态的任何授权逻辑(例如,如果当前已将其分配给主持人)实际上都是属于您的域,而且如果该域不在您的域中,则可以同时更新模型可能会导致最终状态变为无效。例如,如果您使用策略验证所有权,然后去更新该对象-在许多域中,所有权可能会发生更改,并且该操作可能不再有效。
约旦

除非您有非常复杂的协作环境,否则您可能可以在这里使用版本控制实现乐观的并发模型,但是如果您的检查不是在特定的聚合实例中进行的,或者至少不是针对特定的聚合实例进行的,则您的检查可能无法与实际情况保持一致持久更改时对象的状态。
约旦

5

身份验证和授权对于DDD是一个不好的例子。

除非您的公司创建安全产品,否则这些东西都不是域的一部分。

业务或域要求是或应该是“我需要基于角色的身份验证”

然后,您在调用域函数之前检查角色。

如果您有复杂的要求(例如“我可以编辑自己的帖子,但不能编辑其他帖子”),请确保您的域将编辑功能分开EditOwnPost()EditOthersPost()这样您就可以轻松地将角色映射到角色

您也可以将功能分为域对象,例如 Poster.EditPost()Moderator.EditPost()这是一种更为面向对象的方法,尽管您的选择可能取决于您的方法是在域服务还是域对象中。

但是,您选择分离代码,角色映射将在域之外发生。因此,例如,如果您有一个webapi控制器:

PostController : ApiController
{
    [Authorize(Roles = "User")]
    public void EditOwnPost(string postId, string newContent)
    {
        this.postDomainService.EditOwnPost(postId, string newContent);
    }

    [Authorize(Roles = "Moderator")]
    public void EditOtherPost(string postId, string newContent)
    {
        this.postDomainService.EditOtherPost(postId, string newContent);
    }
}

如您所见,尽管角色映射是在宿主层上完成的,但构成您自己他人的帖子的复杂逻辑却是域的一部分。

域识别动作的差异,但是安全性要求仅仅是“功能可以受角色限制”

通过域对象分离可能会更清楚,但是从本质上讲,您正在检查构造对象的方法,而不是调用服务方法的方法。您的要求(如果您仍然希望将其作为域的一部分来表达)将变成“只有主持人才能构造主持人对象”


4
“静态”检查角色有点简单。如果不允许主持人编辑其他主持人的帖子怎么办?此支票不应该属于网域吗?
REDA Housni阿拉维

2
@RédaHousniAlaoui我也想知道这个。除了在域中提及用户/主持人,或者在该ApiController中执行某种逻辑以获取帖子作者的角色之外,我没有想到其他方法来处理此问题。这些都不是对的,根据我的经验,这是很常见的事情,有些明确的指导将非常有帮助。
吉米

1
@Erwan,我正在谈论的用例是动态的。以问候示例为基础的句子“认证和授权是DDD的不良示例”是不诚实的。DDD在这里是为了避免意外的复杂性,并允许管理域的复杂性。动态权限不是偶然的复杂性,也不是现实生活中不会发生的事情。
REDA Housni阿拉维

1
恕我直言,您的解决方案的问题是它不能使客户满意。客户通常希望能够动态更改那些关系。此外,当供应商向不同的公司提供相同的企业软件时,也会发生这种情况。如果该软件的可调整性很差,则供应商最终将死亡。
REDA Housni阿拉维

1
“但是通常认为,安全设置随着时间的推移变得难以管理是一件“坏事”,这实际上意味着您的应用程序变得不安全。通过正确的设计和测试,它是完全可管理的。但是,从我的XP中,要生成正确的设计,域必须检查权限。另一种选择是乌托邦。
REDA Housni阿拉维
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.