扩展授权属性

Extending the Authorize attribute

我实现了一个基于 [Authorize] 属性的 [CustomAuthorization] 属性。我的属性如下所示:

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

我将逻辑(谁接受谁不接受)拆分为分离 class。如果用户根据他的 CustomRoles 属性.

被接受,则方法 AuthorizationSystem.Validate() return 为真

我的控制器看起来像:

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

我正在 运行 应用程序 (C# + WebAPI) 检查它是否工作。

我调试了代码,发现第一个 运行 所需的最低角色级别是 Administrator 而不是 Readonly。因为在不使用任何 CustomRoles 的情况下使用 [CustomAuthorize] 时,它会将 default 行定义为 eUserRole.Administrator。这意味着被调用的第一个 CustomAuthorize 属性是 class 级别的属性, 不是方法级别的属性 .

如何让它调用之前方法(Do())上的属性?

您对 AuthorizeAttribute 同时实现 AttributeIAuthorizationFilter 这一事实感到困惑。您需要做的是创建一个全局注册的 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 }
        );
    }
}

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).

参考:Passive Attributes