Расширение атрибута Authorize

Я реализовал [CustomAuthorization] атрибут на основе [Authorize] приписывать. Мой атрибут выглядит так:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public eUserRole CustomRoles { get; set; } = eUserRole.Administrator; // If not specified, the required role is Administrator

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        AuthorizationSystem auth = new AuthorizationSystem(actionContext.RequestContext.Principal, this.CopyleaksRoles);
        var res = auth.Validate();
        if (!res)
            return false;

        return base.IsAuthorized(actionContext);
    }
}

Я разделил логику (кого принимать, а кого нет) на отдельный класс. Метод AuthorizationSystem.Validate() вернуть true, если пользователь принят согласно его CustomRoles имущество.

Мой контроллер выглядит так:

[CustomAuthorize]
public class MyController : ApiController
{
    [CustomAuthorize(CustomRoles = eUserRole.Readonly)]
    public Response Do()
    {
        // ... Do something ...
    }
}

Я запускаю приложение (C# + WebAPI), чтобы проверить, работает ли оно.

Я отлаживаю код и вижу, что при первом запуске минимальный необходимый уровень роли Administrator вместо Readonly, Потому что при использовании [CustomAuthorize] без всяких CustomRoles, это определить строку по умолчанию, чтобы быть eUserRole.Administrator, Это означает, что первый CustomAuthorize вызываемый атрибут является атрибутом на уровне класса, а не на уровне метода.

Как заставить это вызывать атрибут, который есть у метода (Do()) до?

1 ответ

Решение

Вас смущает тот факт, что AuthorizeAttribute реализует оба Attribute а также IAuthorizationFilter, Что вам нужно сделать, это сделать глобально зарегистрированный IAuthorizationFilter и использовать его, чтобы определить, является ли CustomAuthorizeAttribute существует в действии или контроллере. Затем вы можете определить, что имеет приоритет над другим.

CustomAuthorizeAttribute

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomAuthorizeAttribute : Attribute
{
    public eUserRole CustomRoles { get; set; } = eUserRole.Administrator;
}

CustomAuthoizationFilter

Здесь мы сохраняем некоторые шаги путем создания подклассов AuthorizeAttribute, но мы не собираемся Attribute вообще, только глобально зарегистрированный фильтр.

Наш фильтр содержит код отражения, чтобы определить, какой CustomAuthorize Атрибут имеет приоритет, если оба определены. Это настройка для того, чтобы метод действия переопределял контроллер, но при необходимости вы можете сделать логику более сложной.

public class CustomAuthorizationFilter : AuthorizeAttribute
{
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        if (base.IsAuthorized(actionContext))
        {
            var authorizeAttribute = GetAuthorizeAttribute(actionContext.ActionDescriptor);

            // Attribute doesn't exist - return true
            if (authorizeAttribute == null)
                return true;

            var roles = authorizeAttribute.CustomRoles;

            // Logic - return true if authorized, false if not authorized

        }

        return false;
    }

    private CustomAuthorizeAttribute GetAuthorizeAttribute(HttpActionDescriptor actionDescriptor)
    {
        // Check action level
        CustomAuthorizeAttribute result = actionDescriptor
            .GetCustomAttributes<CustomAuthorizeAttribute>()
            .FirstOrDefault();

        if (result != null)
            return result;

        // Check class level
        result = actionDescriptor
            .ControllerDescriptor
            .GetCustomAttributes<CustomAuthorizeAttribute>()
            .FirstOrDefault();

        return result;
    }
}

использование

Мы регистрируем фильтр глобально. Для каждого запроса CustomAuthorizationFilter сканирует действие и контроллер в запросе, чтобы увидеть, существует ли атрибут. Если так, то тогда запускается логика.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

        // Register our Custom Authorization Filter
        config.Filters.Add(new CustomAuthorizationFilter());

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

ПРИМЕЧАНИЕ. Технически вы можете хранить их обоих в одном классе, но это не так сложно, если вы разделяете их на компоненты, которые фактически есть, а не создаете один класс, который выполняет более одной работы (атрибут и фильтр).

Ссылка: пассивные атрибуты

Другие вопросы по тегам