Несколько атрибутов авторизации, которые знают друг о друге

У меня очень простой сценарий. Я хочу украсить свои контроллеры / действия настраиваемым атрибутом авторизации. Авторизация должна быть предоставлена, если любой из атрибутов действителен. Например,

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

Я не могу объединить параметры в один атрибут авторизации. Приведенный выше пример является только упрощенным примером.

Если какой-либо атрибут авторизует пользователя, я хочу, чтобы пользователь был авторизован. Я предположил, что ActionFilterAttribute или AuthorizeAttribute будут иметь возможность увидеть, какие другие фильтры были выполнены и ожидают выполнения, но не тут-то было.

Как я могу этого добиться? Поскольку атрибуты, похоже, ничего не знают, может быть HttpModule? Кастом ControllerActionInvoker?


person Community    schedule 04.11.2013    source источник
comment
Почему вы хотите построить HttpModule? Когда я взламываю, я бы выбрал msdn.microsoft.com/en-us/library/. Но почему вы не можете комбинировать параметры?   -  person Andreas    schedule 05.11.2013
comment
Я думаю, что если вы используете [MyAuth (Roles = 1)] [MyAuth (Roles = 2)], frameowrk должен знать, что роли 1 и 2 авторизованы. Есть ли конкретная причина, по которой вы не хотите создавать один атрибут авторизации, который принимает несколько параметров?   -  person Spock    schedule 05.11.2013
comment
@Andreas, я не хочу строить HttpModule. Я предлагал это как возможное решение.   -  person    schedule 05.11.2013
comment
@Raj, потому что атрибут принимает 4 параметра. Я не могу объединить их, если я не предоставлю параметры в виде строки для анализа, что нежелательно.   -  person    schedule 05.11.2013
comment
Я думаю, что вы все еще можете добиться желаемого поведения с помощью одного атрибута. Просто интересно, что мешает вам создавать строго типизированные свойства в yr authorize attr? В год настраиваемый атрибут аутентификации. вы можете обработать их соответствующим образом. Или мне что-то здесь не хватает? stackoverflow.com/ questions / 1148312 /   -  person Spock    schedule 05.11.2013


Ответы (3)


Прошлой ночью мне удалось заставить это работать. Мое решение ниже. Атрибут довольно стандартный, и я обрезал фактические части авторизации. Интересные вещи происходят в 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;
    }
}

Чтобы понять это, мне пришлось взглянуть на внутреннее устройство MVC с помощью ILSpy. Для справки, это переопределенная версия этого метода:

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();
    }
}
person Community    schedule 06.11.2013
comment
к сожалению, ApiController не реализует метод InvokeAuthorizationFilters, знаете ли вы, как добиться ИЛИ-поведения в проектах WebApi? - person wodzu; 03.11.2014

Насколько мне известно, вы не можете связать [Authorize] атрибуты желаемым образом, потому что все они должны проходить (И), а не (ИЛИ) поведение. Однако объединение элементов в один не заставляет вас выполнять какие-то магические манипуляции со строкой, независимо от количества параметров, которые вам нужно передать ему. Вы можете определить свой собственный набор параметров, доступных для атрибута 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(){
...
}

Отличный пост о некоторых других соображениях о пользовательской авторизации атрибуты, с которыми я столкнулся, помогая сформулировать этот ответ.

person Tommy    schedule 05.11.2013
comment
Это не ответ на мой вопрос. - person ; 05.11.2013
comment
@Amy - Ваш вопрос - я хочу украсить свое действие несколькими атрибутами авторизации, и если какой-либо из них проходит - то авторизован. Мой ответ - это невозможно (с ресурсами), а также альтернатива, которая показывает, что ваши мысли о наличии единственного утверждения авторизации неверны. - person Tommy; 05.11.2013
comment
Это возможно. Я сделал это вчера вечером и отправляю свое решение в качестве ответа. - person ; 06.11.2013

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() {


}

Посмотрим, уловит ли кто-нибудь упоминание о Бонде. ржу не могу

person Damon Drake    schedule 05.11.2013