ASP.NET MVC 应用程序中针对有条件编辑操作的基于声明的授权设计

6

使用基于声明的模型设计ASP.Net MVC应用程序授权。假设我们有一个名为“Product”的对象。通常有4个不同的操作-创建、编辑、删除和查看。使用ClaimsAuthorize属性进行授权。

[Authorize]
public class ProductController : Controller
{

     [ClaimsAuthorize("Product", "VIEW")]
     public List<Product> GetProducts()
     {
         // ....
     }

     [ClaimsAuthorize("Product", "CREATE")]
     public Product CreateNewProduct(Product product)
     {
         //....
     }
}

然而在我的情况下,我必须支持不同类型的编辑权限:

  1. 如果同一用户最初创建了产品,则某些用户可以编辑该产品

  2. 如果产品属于特定类别并且用户也可以访问相同的类别,则某些用户可以编辑该产品

  3. 某些用户可以编辑所有产品(这是正常的产品编辑操作)

如何优雅地授权所有这些编辑操作(最好是属性驱动,如上所示),同时我希望将授权代码与普通MVC控制器代码和业务逻辑分开。

【上面的代码示例语法不正确,我只是为了解释这个问题而编写的】请告诉我您的想法。

1个回答

5
对于你的第一个问题,基于声明的授权,我已经在这个类似的问题中回答过了。我不会在这里重复。
但是对于你的另一个规则,比如只有所有者才能编辑产品。你可以为每个规则编写单独的AuthorizeAttribute,并将它们应用于你的操作中,考虑以下简单示例:
using Microsoft.AspNet.Identity;
public class OwnerAuthorizeAttribute : AuthorizeAttribute
{
    private string _keyName;
    public bool IsPost { get; set; }

    public OwnerAuthorizeAttribute(string keyName)
    {
        _keyName = keyName;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        // imagine you have a service which could check owner of 
        // product based on userID and ProductID

        return httpContext.User.Identity.IsAuthenticated
            && this.ContainsKey
            && _productService.IsOwner(httpContext.User.Identity.GetUserId(),
                int.Parse(this.KeyValue.ToString()));
    }

    private bool ContainsKey
    {
        get
        {
            return IsPost
                ? HttpContext.Current.Request.Form.AllKeys.Contains(_keyName)
                // for simplicity I just check route data 
                // in real world you might need to check query string too 
                : ((MvcHandler)HttpContext.Current.Handler).RequestContext
                     .RouteData.Values.ContainsKey(_keyName);
        }
    }
    private object KeyValue
    {
        get
        {
            return IsPost
                ? HttpContext.Current.Request.Form[_keyName]
                // for simplicity I just check route data 
                // in real world you might need to check query string too 
                : ((MvcHandler)HttpContext.Current.Handler)
                    .RequestContext.RouteData.Values[_keyName];
        }
    }
}

你可以将同样的模式应用到其他规则中。
你也可以简单地将自定义属性应用于你的操作:
[OwnerAuthorize("id")]
public ActionResult Edit(int id)
{
    // your code
}

[HttpPost]
// double checking in post back too 
[OwnerAuthorize("id", IsPost = true)]
public ActionResult Edit(Product product)
{
    // your code
}

很明显,您可以将多个AuthorizeAttribute应用于操作。在这种情况下,所有它们必须返回true

[ClaimsAuthorize("Product", "EDIT")]
[OwnerAuthorize("id")]
[YetOtherAuthorize]
public ActionResult MyFancyAction(int id)
{
}

谢谢Sam。好答案。 - Andy T
这不是针对MVC 6的,对吗? - user20358
@user20358 不,这是针对Identity 2和MVC 5的,但原则相同,您可以通过在Identity 3和MVC 6中进行一些小调整来使用此方法。 - Sam FarajpourGhamari
OwnerAuthorizeAttribute类中的_productService怎么样?它是通过OwnerAuthorizeAttribute的某个属性进行初始化的吗? - Pavel
@Pavel 这是一个示例,展示了您可以在那里使用自己的逻辑。也许您注入了类或者您喜欢的其他方式。 - Sam FarajpourGhamari
1
这个答案很棒。 - user4573148

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接