【问题标题】:Common place Authorization rules best practices in MVC .NETMVC .NET 中常见的地方授权规则最佳实践
【发布时间】:2015-10-08 14:58:03
【问题描述】:

我目前面临的情况是,我正在使用特定的授权属性装饰我的 HttpPost 控制器操作方法,并在我的 HttpGet 中指定相同的规则以确保某些功能在我的视图中不可用。

是否有最佳实践或更好的解决方案,以便我可以在一个地方指定实际的业务规则以启用 DRY,并且不会陷入代码爆炸的情况,即每个规则都有一个特定的 Authorize 属性..

我现在的情况是这样的:

public class MyController
{

   [HttpGet]
   public ActionResult List()
   {
       // This action is responsible for showing the list of records.  Each record
       // can potentially have a delete link but this is only shown for Administrators

       var viewModel = new MyViewModel()
       {
          CanDeleteRecordRole = Role.Administrator, // Duplicated rule here
          // other properties
       }
   }

   [HttpPost]
   [Authorize(Role.Administrator)]
   public ActionResult Delete(int id)
   {
      /// do stuff
   }
}

public class Role
{
   public const string Administrator = "Administrator";
}

然后在我看来,使用扩展方法我会使用 CanDeleteRecordRole,例如:

@if(Model.DisplayIfAuthorized(Model.CanDeleteRecordRole))
{
   <th>Delete record</th>
}

// and for td columns

@if(Model.DisplayIfAuthorized(Model.CanDeleteRecordRole))
{
   <td>My action link here for deletion</td>
}

【问题讨论】:

  • 不要学究,但我认为我认为在这种情况下很容易提出一个有意义的论点,即控制器操作上的属性提供的保护不是与相同的业务规则的表达@if 标记中的语句。在这种情况下,它们适用于相同的角色,并且在您的代码中位于相同的附近(相同的 V/C),但它们实际上是不同的规则。条件Model.CanDeleteRecordRole 可能适用于与管理员完全不同的角色(如果您需要这种方式)。标记条件更加细化是最好的表达方式。
  • 我想我误读了你显示的控制器和视图的关系,但你仍然想保留你所拥有的。如果您只使用跳过呈现删除链接的部分,您仍然会有一个公开的控制器操作,可以从您预期的身份验证方案之外的客户端调用。这需要一些知识并且可能是恶意的,但以 DRY 原则的名义省略 ATTRIBUTE 可能不是这里的最佳选择。 HTH
  • 谢谢大卫。是的,我不是在考虑放弃属性,而是在考虑如何结合授权要求。例如。如果突然删除成为管理员 + 另一个角色,我宁愿在一个地方更改它,而不是记住要在多个等中进行......
  • 我没有尝试过这种技术,但我绝对感兴趣。当我有机会时,我会尝试一下。它可能与您要查找的内容接近:Don't do Role Based do Activity-based auth checking
  • @DavidTansey 感谢您的链接。我必须消化这篇文章,但可能值得研究。任何事情都可能比我现在想的要好。

标签: asp.net-mvc controller authorization


【解决方案1】:

是的,肯定有一种方法可以将业务逻辑与授权逻辑分离并进行 DRY。该区域称为外部授权管理 (EAM)(请参阅 Gartner's definition)。

要实现 EAM,您需要使用的不仅仅是角色。您需要使用属性本质上只是一个键值对的属性,例如citizenship=Canadianclearance=SECRETdepartment=sales...

角色是不够的。引用之前在cmets中分享的article

基于角色的授权检查有什么问题?

已经使用基于角色的检查创建了大量的授权系统,那么它们有什么问题?很多事情,包括文档和耦合、建模和封装问题,以及需求增长和变化。

基于角色的授权(也称为基于角色的访问控制或RBAC)不够灵活,无法表达丰富的授权场景。您需要求助于 ABAC,即 NIST 定义的 Attribute-Based Access Control 模型。

使用 ABAC,您可以轻松实现以下规则:

  • 用户可以编辑他/她拥有的文档
  • 用户可以查看属于同一部门的所有文档
  • 如果文档是草稿并且文档敏感度等于或小于用户的许可,则具有审阅者角色的用户可以批准文档。

在 ABAC 中可以表达的内容没有限制。

可用于实施 ABAC 的事实上的标准和技术是 XACML,eXtensible Access Control Markup Language。 XACML 定义:

  • 具有以下概念的授权体系结构
    • 达成决策的外部政策决策点 (PDP)
    • 一个策略执行点 (PEP),用于保护您的应用程序/代码/API 并调用 PDP
    • 一个政策信息点 (PIP),用于检索其他属性和元数据。
  • 请求/响应方案:如何提出问题并获得答案,例如Alice 可以查看文档 #123 吗?
  • 一种丰富的策略语言,用于实施我之前给出的示例等策略。

在 XACML 中,所有策略都集中在一个位置。一些好处包括: - 更快的开发时间:您不再需要在应用程序中编写授权代码(if/else) - 更好的安全性:无论语言或技术如何,您都可以在所有应用程序中使用相同的策略。因此,我的回答并非特定于 .NET - 更好的审计能力:如果你将你的 authZ 逻辑移动到一个基于策略的中心点,那么检查它们就更容易了 - 实施 DRY 原则。

这些只是一些可用的好处。

有几种开源和供应商解决方案,例如:

HTH, 大卫。

【讨论】:

    【解决方案2】:

    我过去处理这个问题的方法是创建一个抽象。不是指被允许做事的角色,而是指一个字符串常量,包含被允许做事的角色。像这样:

    public class Role
    {
        public const string DeleteRoles = "Administrator, role2, role3";
        ....
    }
    

    然后创建一个可以解释这些字符串常量的方法(我把这个放在自定义的原理里):

    public class CustomPrincipal
    {
        ...
        public bool IsInRoles(string roles)
        {
            bool authorized = false;
    
            var roles = roles.Split(',');
            foreach (var role in roles)
            {
                if (this.CurrentPrincipal.IsInRole(role)
                {
                    authorized = true;
                    break;
                }
            }
    
            return authorized;
        }
        ...
    }
    

    然后创建一个自定义 Authorization 属性,该属性可以通过 IsInRoles() 方法使用这些字符串常量:

    public class CustomAuthorizeAttribute: AuthorizeAttribute
    {
        public string Roles { get; set; }
    
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var isAuthorized = base.AuthorizeCore(httpContext);
            if (!isAuthorized)
            {
                return false;
            }
    
            isAuthorized = CustomPrincipal.Current.IsInRoles(this.Roles);
    
            return isAuthorized;
        }
    
    }
    

    您在 Action 方法上使用的方法如下:

    [HttpPost]
    [CustomAuthorize(Roles = Role.DeleteRoles)]
    public ActionResult Delete(int id)
    {
        /// do stuff
    }
    

    那么在视图中就可以直接使用IsInRoles()方法了:

    @if(CustomPrincipal.Current.IsInRoles(Role.DeleteRoles))
    {
       <th>Delete record</th>
    }
    

    您可以通过多种方式实现这一点,但关键是抽象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-06
      • 1970-01-01
      • 2021-09-26
      • 2021-07-13
      相关资源
      最近更新 更多