2

我有代表用户授权的类(scala代码):

case class Authorization(
                     userId: UUID,
                     permissions: Seq[String],                     
                     userRoles: Seq[String],
                     companyId: UUID
                   ) {

  private val policy = new Policy(permissions)

  def isAllowed(path: String, action: String): Boolean = policy.isAllowed(path, action)

  def isSuperuser: Boolean = userRoles.contains(SuperUser)

} 

该类在应用程序中的许多地方使用,以检查用户是否对特定实体具有特定权限,例如:

authorization.isAllowed(s"some/entity/path/$id", "read")

为了提高可用性并避免直接的字符串操作,我将这些方法包装在更具体的方法中:

def canViewCompany(companyId:UUID)=authorization.isAllowed(s"company/$companyId","read")

def canViewUser(userId:UUID)=authorization.isAllowed(s"user/$userId","read")

def canEditTask(id:UUID)=authorization.isAllowed(....)

....

以此类推,最多 20 种方法。应该Authorization包含这样的方法canViewCompany()吗?
此类是否有责任了解每个特定实体检查?

更新:所以最终的问题是:

  1. canViewCompany(companyId:UUID)为了提高类的可用性而创建包装器是个好主意 吗?
  2. 如果是这样,应该将这些方法Authorization本身替换还是提取到某些服务中,例如:PermissionService.canViewCompany(id,authorization)=authorization.isAllowed()?
4

4 回答 4

3

在 DDD 中,只有与实体相关的方法才应该位于实体类中,例如createupdate。这种方法只适用于实体属性和字段。据我所知,Authorization是服务而不是实体,因此最好将其定义为其他实体喜欢的服务,User并且Policy应该在其中使用它来执行一些操作,例如isAllowed. 最后,您应该将与此相关的方法Authorization合并到此服务中。

于 2019-09-15T03:26:07.293 回答
1

您提到的那个Authorization服务似乎就像一个工具箱,实际上领域含义很差。您正在尝试使用某种对象,创建某种抽象,将某种行为封装在“通用”服务中。

正如您所说,只要您的域中有新的需求,这个对象就会增长。所以基本上你不得不一直修改你的代码,打破了SOLID的开闭原则

我建议您将授权给定用户的给定操作的责任转移到例中,这样您的域中就会有多个永远不会改变的用例,每个用例都知道自己的授权逻辑。

修改这些用例的唯一原因是该特定用例的授权逻辑是否发生变化,但这很好,因为如果逻辑发生变化,您的域也会发生变化。

将这个责任转移到用例中将有助于您创建更具可扩展性和可扩展性的代码。

当然,视情况而定,但经验法则是尽可能多地将逻辑推送到您的域中,这并不意味着创建大量域服务来存储该逻辑,因为这样您将创建一个贫血的领域模型,你不想要那个。

此外,我建议您应用TELL DON'T ASK原则来保持您的逻辑直截了当。

祝你好运!

于 2019-11-15T19:50:12.147 回答
1

授权在这里看起来不像一个实体——它是一个用户的属性。它的键是 UserID 的事实可以暗示这一点。

更清晰的是

user.IsAllowed(action, path)

或者

user.canView(company)

如何实现取决于实体 - 您可能有授权服务等。

于 2019-10-03T03:12:33.897 回答
0

我的方法是加载具有所有权限的 UserPrincipal(解释 .net),并有一个方法可以检查某些权限是否在Seq[Permissions]. Permission可以是带有pathand的对象action。使用这种方法,您不需要创建一大堆方法。只有一种方法可以验证路径和操作。此外,它允许您在应用程序中传递此对象,而无需担心在每个操作中检查数据库。

简而言之,您应该有一个负责身份验证/授权的服务。此服务创建一个 UserPrincipal ,其中包含您检查访问权限所需的所有数据。

是否有意义?

于 2019-09-15T02:14:50.750 回答