【问题标题】:Where to verify authorization for a Command?在哪里验证命令的授权?
【发布时间】:2013-06-14 03:10:08
【问题描述】:

问题的标题几乎恢复了:我在哪里验证命令的授权?

例如,将客户设置为首选涉及:

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

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

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

SomeView.cshtml

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

客户控制器

[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 的建议封装命令以添加行为(授权、验证、日志记录)更容易实现。

我找到了this blog post,它准确地解释了这个概念。

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

【问题讨论】:

    标签: authorization cqrs


    【解决方案1】:

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

    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
        }
    
    }
    

    【讨论】:

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

    在 View 中进行验证是很好的 UI,因此用户不会误点击它。我认为控制器验证是“真实的”,因为那里是创建命令的地方。如果用户没有权限,她应该无法创建(甚至执行该操作)命令。

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

    【讨论】:

    • 在命令处理程序中进行授权检查以允许将域与其他服务重用是值得的。例如,使用 ASP.NET Web API 提供 API。您可以创建一个命令处理程序装饰器,在每个命令处理程序之前调用该装饰器以“自动”调用授权服务,并将该命令作为要验证的操作。
    • 我几乎已经准备好检查 UI 和控制器的授权,但正如 Dennis 建议的那样,在实际处理之前执行此操作的包装命令(如果它看起来很容易实现)提供了另一层验证免费。正如@Ben Smith 所说,重用命令是我没有想到的另一个好处。
    • @BenSmith 实际上,服务总线支持前后命令处理程序操作并不是一个坏主意,类似于 asp.net mvc 过滤器。
    猜你喜欢
    • 2023-04-09
    • 1970-01-01
    • 2013-01-01
    • 1970-01-01
    • 2018-10-31
    • 1970-01-01
    • 2023-01-29
    • 1970-01-01
    • 2021-12-22
    相关资源
    最近更新 更多