【问题标题】:How to handle authorization that is dependent on the requested data如何处理依赖于请求数据的授权
【发布时间】:2018-08-28 21:23:21
【问题描述】:

我有一个 API 项目,它有很多访问限制,具体取决于用户请求的项目。例如,假设我有这样的 Action:

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

软件正在增长,并且开始变得嘈杂且不干净。这些动作有 90% 的权限检查和 10% 的实际代码。

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

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

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

对于我所遗漏的此类问题,是否有任何指导方针?

【问题讨论】:

  • IActionFilter 怎么样,例如AuthorizeAttribute 的扩展。您甚至可以在全局范围内注册 ActionFilter。

标签: c# asp.net design-patterns asp.net-core architecture


【解决方案1】:

我建议制作某种授权服务来处理您需要的一切。例如:

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

当然,您可以添加其他方法进行授权,这些方法将对某些其他实体(不仅是项目)进行检查。

【讨论】:

    【解决方案2】:

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

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

    考虑使用此图像进行可视化:

    伪可能是这样的:

    1. 检查登录的用户角色类型
    2. 根据用户角色类型获取每个特性或功能列表的允许 ID
    3. 加载该用户的特性或功能

    注意:可能存在适用于所有类型用户的通用功能列表。

    【讨论】:

      【解决方案3】:

      您有两种方法来检查权限,首先是应用程序端(控制器不是很好的选择,如果您至少有助手或 BaseController 会很好)。您可以尝试在基本控制器中覆盖 OnActionExecuting 并检查那里的权限

      第二个:

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

      【讨论】:

        【解决方案4】:
        • 控制器的动作应该有简单的happy-pass逻辑。
        • 任何基于请求参数的permission checks/validations逻辑都应该使用middlewares/filters解耦。
        • 如果验证检查不依赖于请求参数,那么最好将逻辑移至依赖服务并引发异常以停止进一步的请求执行。
        • 并且应该使用中间件/过滤器来解耦异常处理。

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

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

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

        【讨论】:

          猜你喜欢
          • 2020-04-27
          • 2019-05-14
          • 1970-01-01
          • 2020-02-05
          • 2014-12-01
          • 1970-01-01
          • 2014-07-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多