C# 对于API上的细粒度授权,哪种是正确的授权方法?
我在一个有多个安全层的网站上工作。最基本的安全级别由自定义授权属性处理,该属性验证用户是否处于正确的角色等 我们用Authorize方法装饰控制器中的方法,这防止未经授权的用户访问这些调用 然而,我们也有一些细粒度的授权要求。例如,公司A的用户不能修改公司B的任何用户。因此,虽然公司A和公司B的管理员都有权从API调用“UpdateUser”,但公司A应该不能更新公司B的一个用户 目前,跨公司的访问限制正在控制器内部处理。我遇到的问题是,我们引入的每一种新方法要么包含代码的复制粘贴,要么包含非常类似的变体 也就是说,我也不确定它是否属于服务(我们称之为UserService) 因此,我的选择是: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的一个用户 目前,跨公司的访问限制正在控制器内部处理。我遇到的问题是,我们引入的每一种新方法要么包含代码的复制粘贴,要么包含非常
对于这些类型的细粒度权利,哪些模式被认为是“最佳实践”;我个人只会在行动中保留访问检查。您始终可以尝试使用其他函数和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%的逻辑进行单元测试