C# 如何处理依赖于请求数据的授权

C# 如何处理依赖于请求数据的授权,c#,asp.net,design-patterns,asp.net-core,architecture,C#,Asp.net,Design Patterns,Asp.net Core,Architecture,我有一个API项目,根据用户请求的项目有很多访问限制。例如,假设我有这样一个动作: [HttpPut] public async Task<IActionResult> EditItem([FromBody]Item editedItem) { var item = await db.Items.SingleOrDefaultAsync(i => i.Id == editedItem.Id); if (item == null) // There is alr

我有一个API项目,根据用户请求的项目有很多访问限制。例如,假设我有这样一个动作:

[HttpPut]
public async Task<IActionResult> EditItem([FromBody]Item editedItem)
{
    var item = await db.Items.SingleOrDefaultAsync(i => i.Id == editedItem.Id);

    if (item == null) // There is already pre-filter on the DB.
        return Unauthorized();

    if (item.Status == ItemStatus.Closed)
        return Unauthorized();

    if (item.Owner != _currentUser)
        return Unauthorized();

    // Say the normal user only have permissions to edit items on the Status "Added"
    if (!(_currentUser is Manager) && editedItem.Status == item.Status.Open)
        return Unauthorized();

    // Edit the data...

    await db.SaveChangesAsync();

    return Ok();
}
[HttpPut]
公共异步任务EditItem([FromBody]项EditItem)
{
var item=await db.Items.SingleOrDefaultAsync(i=>i.Id==editItem.Id);
if(item==null)//数据库上已存在预过滤器。
未经授权返回();
如果(item.Status==ItemStatus.Closed)
未经授权返回();
如果(item.Owner!=\u currentUser)
未经授权返回();
//假设普通用户仅具有编辑状态为“已添加”的项目的权限
如果(!(_currentUser是管理者)&&editem.Status==item.Status.Open)
未经授权返回();
//编辑数据。。。
等待db.saveChangesSync();
返回Ok();
}
软件正在增长,并且开始变得嘈杂和不干净。这些操作有90%的权限检查和10%的实际代码

在每个控制器中,我都有许许多多的权限检查。我已经在使用身份验证和授权;事实并非如此。这里,它取决于用户请求访问的项目

我想做的是从控制器中删除检查并降低代码的复杂性

我已经尝试创建自定义策略、自定义属性和自定义中间件,但是两次检查数据库的速度要慢得多,而且对降低复杂性几乎没有什么作用


关于这类问题,是否有我所缺少的指导原则?

您有两种方法来检查权限,第一种是应用程序端(controller不是一个好选择,如果您至少有helper或BaseController,这将是一个好选择)。您可以尝试覆盖在基本控制器中执行的OnActionExecuting,并在那里检查权限

第二:


如果您使用的是Mssql数据库或类似的数据库,您可以在数据库端(创建函数或存储过程)检查权限,它返回0或1(0表示用户没有权限)

我建议提供某种授权服务,以处理您所需的一切。例如:

public class AuthorizationService
{
    ...

    public Task<bool> IsAuthorizedAsync(Item item)
    {
        var authorized = item != null 
                            && item.Status != ItemStatus.Closed 
                            && item.Owner == _currentUser 
                            && (!(_currentUser is Manager) && editedItem.Status == item.Status.Open);
        return Task.FromResult(authorized);
    }
}
公共类授权服务
{
...
公共任务IsAuthorizedAsync(项目)
{
var authorized=item!=null
&&item.Status!=ItemStatus.Closed
&&item.Owner==\u当前用户
&&(!(_currentUser是管理者)&&editem.Status==item.Status.Open);
返回任务.FromResult(已授权);
}
}
当然,您可以添加其他授权方法,这些方法将对一些其他实体(不仅仅是项目)进行检查。

  • 控制器的动作应该具有简单的愉快传递逻辑
  • 任何基于请求参数的
    权限检查/验证
    逻辑都应该使用/来解耦
  • 如果验证检查不依赖于请求参数,那么最好将逻辑移到依赖项服务并引发异常以停止进一步的请求执行
  • 异常处理应该通过使用中间件/过滤器来解耦

但是检查数据库两次要慢得多

您可以在
HttpContext.Items
中存储来自第一个数据库调用(比如在中间件中)的数据,然后在实际操作中只需从上下文而不是数据库中获取数据

HttpContext.Items
:获取或设置可用于共享此请求范围内数据的键/值集合


考虑到您有5种类型的用户角色。根据一组规则,每个角色都有自己的视图

现在考虑一下,你有20个功能。 用户“角色类型1”将获得许多功能,但不是所有功能。因此,一旦你创建了你的功能,你就需要让它们变得简单、负责、独特和个性化

将此图像视为您的可视化:

伪函数可以是这样的:

[HttpPut]
public async Task<IActionResult> EditItem([FromBody]Item editedItem)
{
    var item = await db.Items.SingleOrDefaultAsync(i => i.Id == editedItem.Id);

    if (item == null) // There is already pre-filter on the DB.
        return Unauthorized();

    if (item.Status == ItemStatus.Closed)
        return Unauthorized();

    if (item.Owner != _currentUser)
        return Unauthorized();

    // Say the normal user only have permissions to edit items on the Status "Added"
    if (!(_currentUser is Manager) && editedItem.Status == item.Status.Open)
        return Unauthorized();

    // Edit the data...

    await db.SaveChangesAsync();

    return Ok();
}
  • 检查已登录的用户角色类型
  • 根据用户角色类型获取每个功能或功能列表的允许ID
  • 加载该用户的特性或功能

  • 注:可能有一个适用于所有用户类型的通用功能列表

    如何使用
    IActionFilter
    ,例如
    authorized属性的扩展。您甚至可以全局注册ActionFilters。