Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/35.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 对于API上的细粒度授权,哪种是正确的授权方法?_C#_Asp.net_Asp.net Web Api_Asp.net Web Api2 - Fatal编程技术网

C# 对于API上的细粒度授权,哪种是正确的授权方法?

C# 对于API上的细粒度授权,哪种是正确的授权方法?,c#,asp.net,asp.net-web-api,asp.net-web-api2,C#,Asp.net,Asp.net Web Api,Asp.net Web Api2,我在一个有多个安全层的网站上工作。最基本的安全级别由自定义授权属性处理,该属性验证用户是否处于正确的角色等 我们用Authorize方法装饰控制器中的方法,这防止未经授权的用户访问这些调用 然而,我们也有一些细粒度的授权要求。例如,公司A的用户不能修改公司B的任何用户。因此,虽然公司A和公司B的管理员都有权从API调用“UpdateUser”,但公司A应该不能更新公司B的一个用户 目前,跨公司的访问限制正在控制器内部处理。我遇到的问题是,我们引入的每一种新方法要么包含代码的复制粘贴,要么包含非常

我在一个有多个安全层的网站上工作。最基本的安全级别由自定义授权属性处理,该属性验证用户是否处于正确的角色等

我们用Authorize方法装饰控制器中的方法,这防止未经授权的用户访问这些调用

然而,我们也有一些细粒度的授权要求。例如,公司A的用户不能修改公司B的任何用户。因此,虽然公司A和公司B的管理员都有权从API调用“UpdateUser”,但公司A应该不能更新公司B的一个用户

目前,跨公司的访问限制正在控制器内部处理。我遇到的问题是,我们引入的每一种新方法要么包含代码的复制粘贴,要么包含非常类似的变体

也就是说,我也不确定它是否属于服务(我们称之为UserService)

因此,我的选择是:

  • 在控制器内部进行检查,查看请求更新的用户是否与被更新的用户属于同一家公司

  • 在服务内部执行相同的检查

  • 社区的任何建议

  • 第二种方法的优点是代码不需要在控制器中重复。当多个控制器调用同一个服务方法时,好处是显而易见的——底层检查是一致的,代码不会重复

    第二种方法的缺点是,现在我的服务需要了解经过身份验证的用户。这给单元测试带来了一些问题


    对于这些类型的细粒度权利,哪些模式被认为是“最佳实践”;我个人只会在行动中保留访问检查。您始终可以尝试使用其他函数和lambda对其进行泛化,如:

    return IfAccessIsGood("CompanyA","anotherparam",()=>{ /* do something  */ });
    
    但以下是我最初为您准备的完整回复:

    听起来您有一些参数,这些参数在控制器操作中发生了更改,这些参数指定了哪个公司,您可以根据该公司执行访问检查,例如:

    [AuthorizeCustom]
    [Route("{company}/{something}")]
    [HttpPost]
    public async Task<IHttpActionResult> DoSomething(string company, string something) {
        if(company == "A" && something == "NOTA") 
        {
          return BadRequest("Your company can't do that!!!");
        }
        else if(company == "B" && something == "NOTB")
        {
           return BadRequest("Your company can't do that!!!");
        }
        else {} // do the thing
    }
    
    [授权自定义]
    [路线(“{company}/{something}”)]
    [HttpPost]
    公共异步任务DoSomething(字符串公司、字符串某物){
    如果(公司=“A”&&something=“NOTA”)
    {
    返回错误请求(“您的公司不能这样做!!!”;
    }
    如果(公司==“B”&&something==“NOTB”)
    {
    返回错误请求(“您的公司不能这样做!!!”;
    }
    否则{}//做这件事
    }
    
    显然,您的实现可能看起来更加优雅,但我认为它在幕后也在做类似的事情

    如果必须从根本上区别对待公司访问,您可以创建一个基类,该基类继承自ApicController,并将CompanyA和CompanyB实现作为控制器来实现该基类,这将使它们完全分开。然后,您可以重写一个检查访问的访问函数,并为您公开的api实现每个方法。您的路线将不再是可变的,而是:

    [Route(“CompanyA/{something}”)]

    [路由(“CompanyB/{something}”)]

    它们将各自驻留在从基继承的自己的类中,因此允许您进行所需的任何控制,而实际上不必复制太多代码

    这确实很棘手,因为属性不能被继承(至少不能通过通常的方式),但您可以执行以下操作:

    [AuthorizeCustom]
    [RoutePrefix("CompanyA")]
    public class CompanyA : BaseClass
    {
      [Route("{something}")]
      [HttpPost]
      override async Task<IHttpActionResult> DoSomething(string something) { return await base.DoSomething(something); }
    
      override bool CheckAccess(string something) { 
        if(something != "A") return false;
        else return true;
      }
    }
    
    [授权自定义]
    [RoutePrefix(“公司”)]
    公共类公司a:基本类
    {
    [路线(“{something}”)]
    [HttpPost]
    重写异步任务DoSomething(字符串something){return wait base.DoSomething(something);}
    重写布尔检查访问(字符串){
    如果(某物!=“A”)返回false;
    否则返回true;
    }
    }
    
    通过标准化访问并简单地重写属性以获取公司价值,而不是在每个公司实现中重写访问,您可以使这一点变得更加优雅,而且这仍然不是一个很好的解决方案,除非您可以通过重写集中式功能找到其他好处

    老实说,尽管如此,我可能还是停留在控制器动作本身的逻辑上。如果您想轻松地进行单元测试,请将操作逻辑的核心放在其他位置,并在操作中简单地返回其值,这样您就可以在单独的函数/类中对99%的逻辑进行单元测试