Extending the Authorize attribute

主宰稳场 提交于 2019-12-04 20:00:17

You are getting confused by the fact that AuthorizeAttribute implements both Attribute and IAuthorizationFilter. What you need to do is make a globally registered IAuthorizationFilter and use it to determine whether the CustomAuthorizeAttribute exists on the action or controller. Then you can determine which takes precedence over the other.

CustomAuthorizeAttribute

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

CustomAuthoizationFilter

Here we save some steps by subclassing AuthorizeAttribute, but we don't intend for this to be an Attribute at all, only a globally registered filter.

Our filter contains Reflection code to determine which CustomAuthorize attribute takes precedence if both are defined. This is setup to make the action method override the controller, but you can make the logic more complex, if needed.

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;
    }
}

Usage

We register the filter globally. For each request, CustomAuthorizationFilter scans the action and controller in the request to see if the attribute exists. If so, it then runs the logic.

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 }
        );
    }
}

NOTE: Technically you can keep them both in the same class, but it is less confusing if you separate them into the components that that actually are rather than making a single class that does more than one job (attribute and filter).

Reference: Passive Attributes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!