【问题标题】:Multiple authorization attributes that are aware of each other相互感知的多个授权属性
【发布时间】:2013-11-15 16:35:20
【问题描述】:

我有一个非常简单的场景。我想用自定义授权属性装饰我的控制器/操作。如果 any 属性有效,则应授予授权。例如,

[MyAuth(1)]
[MyAuth(2)]
public class MyController : Controller
{
    ...
}

我无法将这些参数组合成一个单一的授权属性。上面的例子只是一个简化的例子。

如果任一属性授权用户,我希望用户被授权。我假设ActionFilterAttributeAuthorizeAttribute 将有办法查看其他过滤器已执行并等待执行,但没有这样的运气。

我怎样才能做到这一点?由于属性似乎没有任何意识,也许是HttpModule?自定义ControllerActionInvoker

【问题讨论】:

  • 你为什么要建立一个HttpModule?当我破解时,我会选择 msdn.microsoft.com/en-us/library/… 。但是为什么不能组合参数呢?
  • 我认为如果您使用 [MyAuth(Roles = "1")] [MyAuth(Roles = "2")],框架应该知道角色 1 和角色 2 已授权。您是否有特殊原因要避免创建接受多个参数的单个 Authorize 属性?
  • @Andreas,我不想建立一个HttpModule。我把它作为一种可能的解决方案扔出去了。
  • @Raj,因为该属性需要 4 个参数。我不能组合它们,除非我将参数作为要解析的字符串提供,这是不可取的。
  • 我认为您仍然可以通过一个属性实现所需的行为。只是想知道是什么阻止了您在授权 attr 中创建强类型属性?在您的自定义身份验证属性中。您可以相应地处理它们。或者我在这里错过了什么? stackoverflow.com/questions/1148312/…

标签: c# asp.net-mvc asp.net-mvc-4 authorize-attribute


【解决方案1】:

我昨晚设法让它工作。我的解决方案如下。该属性非常标准,我已经修剪了实际的授权部分。有趣的事情发生在HasAssignedAcccessActionInvoker

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class RequiresAssignedAccess : AuthorizeAttribute
{
    public int AccessType { get; private set; }
    public int IdType { get; private set; }
    public int IdValue { get; private set; }
    public int Level { get; private set; }

    public RequiresAssignedAccess(int accessType, int idType, int idValue, int level)
    {
        ...
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (!base.AuthorizeCore(httpContext))
            return false;

        bool retval = ...

        return retval;
    }
}

HasAssignedAcccessActionInvoker 继承自标准操作调用程序,但我重写了 InvokeAuthorizationFilters 方法以添加我们需要的授权逻辑。标准调用程序只是通过授权过滤器旋转,如果它们中的任何一个返回结果,它就会中断循环。

public class HasAssignedAcccessActionInvoker : ControllerActionInvoker
{
    protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
    {
        AuthorizationContext authCtx = new AuthorizationContext(controllerContext, actionDescriptor);

        /*
         * If any of the filters are RequiresAssignedAccess, default this to false.  One of them must authorize the user.
         */
        bool hasAccess = !filters.Any(f => f is RequiresAssignedAccess);

        foreach (IAuthorizationFilter current in filters)
        {
            /*
             * This sets authorizationContext.Result, usually to an instance of HttpUnauthorizedResult
             */
            current.OnAuthorization(authCtx);

            if (current is RequiresAssignedAccess)
            {
                if (authCtx.Result == null)
                {
                    hasAccess = true;
                }
                else if (authCtx.Result is HttpUnauthorizedResult)
                {
                    authCtx.Result = null;
                }

                continue;
            }

            if (authCtx.Result != null)
                break;
        }

        if (!hasAccess && authCtx.Result == null)
            authCtx.Result = new HttpUnauthorizedResult();

        return authCtx;
    }
}

我必须使用 ILSpy 查看 MVC 的内部结构才能弄清楚这一点。作为参考,这是该方法的重写版本:

protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
    AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
    foreach (IAuthorizationFilter current in filters)
    {
        current.OnAuthorization(authorizationContext);
        if (authorizationContext.Result != null)
        {
            break;
        }
    }
    return authorizationContext;
}

最后,为了连接它并使一切成为可能,我们的控制器继承自 BaseController,它现在返回新的调用程序。

public class BaseController : Controller
{
    protected override IActionInvoker CreateActionInvoker()
    {
        return new HasAssignedAcccessActionInvoker();
    }
}

【讨论】:

  • 很遗憾ApiController没有实现InvokeAuthorizationFilters方法,你知道如何在WebApi项目上实现“OR-behaviour”吗?
【解决方案2】:

据我所知,您不能以您想要的方式链接[Authorize] 属性,因为它们都必须传递 (AND) 而不是 (OR) 行为。但是,将这些项目组合为一个不会导致您必须进行一些魔术字符串操作,无论您需要传递给它的参数数量如何。您可以定义自己的一组可用于Authorize 属性的参数。

public class SuperCoolAuthorize : AuthorizationAttribute
{
    public string Parameter1{get;set;}
    public string Parameter2{get;set;}
    public int Parameter3{get;set;}
    public string Parameter4{get;set;}
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // your custom behaviour
    }
}

在你的控制器/动作方法上

[Authorize(Parameter1 = "Foo", Parameter2 = "Bar", Parameter3 = 47, Parameter4 = string.Empty)
public ActionResult MyControllerAction(){
...
}

A great post on some other considerations 关于我在帮助制定此答案时遇到的自定义授权属性。

【讨论】:

  • 这不能回答我的问题。
  • @Amy - 你的问题是 - 我想用多个授权属性来装饰我的动作,如果有任何一个通过 - 然后授权。我的回答 - 这是不可能的(有资源),以及表明您对拥有单一授权声明的想法的替代方案是不正确的。
  • 有可能。我昨晚做到了,并提交了我的解决方案作为答案。
【解决方案3】:
public class AuthUserAttribute : AuthorizeAttribute {

public string[] SecurityGroups;
public string Groups { get; set; }

protected override bool AuthorizeCore(HttpContextBase httpContext) {
  bool valid = false;

  var user = UserInformation.Current;

  if (user.SecurityGroups.Select(x => x).Intersect(this.SecurityGroups).Any()) {
    valid = true;
  }

  if (user.SecurityGroups.Select(x => x).Intersect(new string[] { "IT Administrators" }).Any()) {
    valid = true;
  }

  return valid;
}

public override void OnAuthorization(AuthorizationContext filterContext) {
  if (!this.AuthorizeCore(filterContext.HttpContext)) {
    if (UserInformation.Current.SecurityGroups.Count == 0) {
      filterContext.Result = new RedirectResult(string.Format("/oa?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
    }
    else {
      filterContext.Result = new RedirectResult(string.Format("/oa/user/permissions?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
    }
  }
  else {
    base.OnAuthorization(filterContext);
  }
}

}

然后我用装饰

[AuthUser(SecurityGroups = new string[] { "Data1", "Data2" })]
public ActionResult ForYourEyesOnly() {


}

我们会看看是否有人收到 Bond 参考资料。哈哈

【讨论】:

    猜你喜欢
    • 2013-06-20
    • 2014-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    相关资源
    最近更新 更多