【问题标题】:Which is the correct authorization approach for fine grained entitlements on an API?API 上细粒度权利的正确授权方法是什么?
【发布时间】:2017-05-17 13:21:04
【问题描述】:

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

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

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

目前,跨公司的访问限制是在控制器内部处理的。我遇到的问题是,我们引入的每个新方法都包含代码的复制和粘贴或非常相似的变体。

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

所以我的选择是:

  1. 检查控制器内部,查看请求更新的用户是否与被更新用户属于同一公司

  2. 在服务内部执行相同的检查。

  3. 来自社区的任何建议。

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

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

对于这些类型的细粒度权利,哪些模式被认为是“最佳实践”?

【问题讨论】:

    标签: c# asp.net asp.net-web-api asp.net-web-api2


    【解决方案1】:

    TLDR;我个人只会在操作中保留访问检查。您可以随时尝试使用其他函数和 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
    }
    

    显然,您的实现可能看起来更加优雅,但我认为它在幕后做了类似的事情。

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

    [Route("CompanyA/{something}")]

    [Route("CompanyB/{something}")]

    这些都将驻留在从 Base 继承的自己的类中,因此允许您进行所需的任何控制,而实际上不必复制大量代码。

    这确实很棘手,因为属性不能被继承(至少不能通过通常的方式),但你可以这样做:

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

    您可以通过标准化访问权限并简单地覆盖一个属性以获取公司价值而不是在每个公司实现中覆盖访问权限来使这更加优雅,这仍然不是一个很好的解决方案,除非您可以通过以下方式找到一些其他好处覆盖集中式功能。

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

    【讨论】:

      猜你喜欢
      • 2018-01-16
      • 2019-03-28
      • 1970-01-01
      • 2011-12-20
      • 1970-01-01
      • 1970-01-01
      • 2011-06-23
      • 2023-04-01
      • 2013-02-14
      相关资源
      最近更新 更多