问题 在哪里验证命令的授权?


问题的标题重新开始:我在哪里验证命令的授权?

例如,将客户设置为首选包括:

  • MarkAsPreferred 控制器动作(可以是Winforms或其他);
  • SetCustomerAsPreferredCommand;
  • SetCustomerAsPreferredCommandHandler;
  • Customer.MarkAsPreferred() (域);

我确定了3个地方来检查授权:

  • UI 用于显示目的(如果用户无法访问链接/按钮,则用户不应该看到它);
  • 控制器动作 验证用户是否有权调用该命令;假设命令总是成功(关于验证,但我也假设授权),我们有机会告知用户缺乏访问权限;
  • 在命令内 在调用域逻辑之前;

SomeView.cshtml

if (authorizationService.Authorize("MarkCustomerAsPreferred))
{
    // show link
}

CustomerController

[HttpPost]
public ActionResult MarkAsPreferred(Guid id)
{
    if (!authorizationService.Authorize("MarkCustomerAsPreferred))
    {
        return RedirectToAction("Unauthorized");
    }

    var MarkCustomerAsPreferredCommand { Id = id };
    ...
}

MarkCustomerAsPreferredCommandHandler

public void Handle(MarkCustomerAsPreferredCommand command)
{
    if (!authorizationService.Authorize("MarkCustomerAsPreferred"))
    {
        throw new Exception("...");
    }

    customer.MarkAsPreferred();
}

我的问题是:我 需要 验证3个地方的授权或者我只是过分热心?

我搜索了整个互联网,但没有找到任何关于这个的例子或参考。

编辑

经过更多研究和一些测试后,我认为包装命令以添加行为(授权,验证,日志记录),如Dennis Taub所建议的那样,实施起来更容易,更清晰。

我发现 这篇博文 这恰恰解释了这个概念。

关于为一个命令提供多个处理程序,我不需要为每个原始命令的每个行为实现一个命令处理程序,一个包装命令可以包装所有处理程序。


1812
2018-06-18 02:47


起源



答案:


我认为最终授权应该在应用程序服务级别上完成,即作为处理命令的一部分。例如,您可以使用授权处理程序包装命令处理程序。

class AuthorizationHandler : IHandle<SetCustomerAsPreferred> {

    IHandle<SetCustomerAsPreferred> innerHandler;

    public AuthorizationHandler(IHandle<SetCustomerAsPreferred> handler)
    {
        innerHandler = handler;
    }

    public void Handle(SetCustomerAsPreferred command) 
    {
        if (/* not authorized */)
            throw ...
        innerHandler.Handle(command);
    }

}

class SetCustomerAsPreferredCommandHandler : IHandle<SetCustomerAsPreferred> {

    public void Handle(SetCustomerAsPreferred command) 
    {
        // do the work
    }

}

5
2018-06-18 15:10



包装命令是一种添加功能的好方法,但它提出了几个问题:1)这开辟了很多可能性,例如日志记录和验证;所以对于每个命令,我将实现N个命令处理程序(实际处理程序,日志记录,验证,授权等)? 2)现在我有两个或更多处理程序用于同一个命令,我的IoC容器如何正确设置它们以注入控制器? 3)在处理命令之前,我是否仍应在控制器中检查授权,以尽量减少不被接受的风险? - Luiz Damim
从我的观点来看,这种方法有点臭,因为你为同一个命令定义了2个处理程序。我认为应该在发出命令时进行验证检查。至少用asp.net mvc我有一个授权过滤器(也可能适用于web api)。这是一个偏好的问题。 @LuizDamim至少我的ServiceBus(我最近写的)可以选择在进行自动配置时忽略某些类型,可能你正在使用的服务总线也有这个选项。 - MikeSW
@LuizDamim 1)记录不应该作为命令处理程序完成,我的意思是任何消息处理程序都可以采用记录器依赖。验证在两个目的中完成,具有不同的目的:您在控制器级别完成输入验证,业务规则验证由域对象本身完成。 3)使用asp.net mvc我会说如果用户无权发出该命令,则不应该达到该操作。 - MikeSW
@MikeSW我不确定我理解你的担忧。即使链接处理程序,从外部角度来看,每个命令仍然只有一个处理程序。 API是统一的,它基本上归结为管道和过滤器类型的方法。 - Dennis Traub
我不直接链接处理程序。我链命令 - >处理程序 - >命令/事件 - >处理程序。给定命令总是只有一个处理程序。 IMO处理程序有责任直接管理一个特定的上下文。您的方法有效,但IMO令人困惑。在所有真正的处理程序不是Auth之后。只是你正在使用一个处理程序来实现过滤器功能(它没有真正处理命令)。 - MikeSW


答案:


我认为最终授权应该在应用程序服务级别上完成,即作为处理命令的一部分。例如,您可以使用授权处理程序包装命令处理程序。

class AuthorizationHandler : IHandle<SetCustomerAsPreferred> {

    IHandle<SetCustomerAsPreferred> innerHandler;

    public AuthorizationHandler(IHandle<SetCustomerAsPreferred> handler)
    {
        innerHandler = handler;
    }

    public void Handle(SetCustomerAsPreferred command) 
    {
        if (/* not authorized */)
            throw ...
        innerHandler.Handle(command);
    }

}

class SetCustomerAsPreferredCommandHandler : IHandle<SetCustomerAsPreferred> {

    public void Handle(SetCustomerAsPreferred command) 
    {
        // do the work
    }

}

5
2018-06-18 15:10



包装命令是一种添加功能的好方法,但它提出了几个问题:1)这开辟了很多可能性,例如日志记录和验证;所以对于每个命令,我将实现N个命令处理程序(实际处理程序,日志记录,验证,授权等)? 2)现在我有两个或更多处理程序用于同一个命令,我的IoC容器如何正确设置它们以注入控制器? 3)在处理命令之前,我是否仍应在控制器中检查授权,以尽量减少不被接受的风险? - Luiz Damim
从我的观点来看,这种方法有点臭,因为你为同一个命令定义了2个处理程序。我认为应该在发出命令时进行验证检查。至少用asp.net mvc我有一个授权过滤器(也可能适用于web api)。这是一个偏好的问题。 @LuizDamim至少我的ServiceBus(我最近写的)可以选择在进行自动配置时忽略某些类型,可能你正在使用的服务总线也有这个选项。 - MikeSW
@LuizDamim 1)记录不应该作为命令处理程序完成,我的意思是任何消息处理程序都可以采用记录器依赖。验证在两个目的中完成,具有不同的目的:您在控制器级别完成输入验证,业务规则验证由域对象本身完成。 3)使用asp.net mvc我会说如果用户无权发出该命令,则不应该达到该操作。 - MikeSW
@MikeSW我不确定我理解你的担忧。即使链接处理程序,从外部角度来看,每个命令仍然只有一个处理程序。 API是统一的,它基本上归结为管道和过滤器类型的方法。 - Dennis Traub
我不直接链接处理程序。我链命令 - >处理程序 - >命令/事件 - >处理程序。给定命令总是只有一个处理程序。 IMO处理程序有责任直接管理一个特定的上下文。您的方法有效,但IMO令人困惑。在所有真正的处理程序不是Auth之后。只是你正在使用一个处理程序来实现过滤器功能(它没有真正处理命令)。 - MikeSW


在View中进行验证是一个很好的用户界面,因此用户不会错误地点击它。我认为控制器验证是“真正的”验证,因为存在命令的创建位置。如果用户没有权限,则她应该无法创建(甚至达到该操作)该命令。

我认为将检查放在处理程序中有点过分,因为授权并不是它的责任,并且不像用户可以直接访问该处理程序。


4
2018-06-18 06:12



值得在命令处理程序中进行授权检查以允许将域重用于其他服务。例如,使用ASP.NET Web API提供API。您可以创建一个命令处理程序装饰器,在每个命令处理程序之前调用它以“自动”使用该命令调用授权服务作为验证操作。 - Ben Smith
我几乎开始检查UI和控制器上的授权,但正如Dennis建议的那样,在实际处理之前执行此操作的包装命令(如果它看起来很容易实现)会给出另一层验证 免费。重新使用@Ben史密斯所说的命令是我没有想到的另一个好处。 - Luiz Damim
@BenSmith实际上,服务总线支持前后命令处理程序操作并不是一个坏主意,类似于asp.net mvc过滤器。 - MikeSW