获取MVC AllowAnonymous以覆盖自定义授权属性

时间:2018-01-22 12:46:47

标签: asp.net-mvc

我创建了一个自定义授权属性,但我需要一些操作来允许匿名访问。我尝试了三种不同的方法但未成功:使用AllowAnonymous,使用其他参数更新现有属性,并创建新的覆盖属性。基本上似乎控制器级属性总是在动作级属性之前被调用。

这是控制器:

[AuthorizePublic(Sites = AuthSites.Corporate)]
public class CorporateController : SecuredController
{
    [AuthorizePublic(Sites = AuthSites.Corporate, AllowAnonymous = true)]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View();
    }
}

属性:

public class AuthorizePublic : AuthorizeAttribute
{
    public AuthSites Sites { get; set; }
    public bool AllowAnonymous { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        // Logic
    }        
}

作为最后的手段,我可​​以将登录操作移动到他们自己的控制器上,但在我这样做之前,我是否遗漏了一些东西以使其中一种方法起作用?我有点惊讶于动作级属性不会覆盖控制器级属性。

1 个答案:

答案 0 :(得分:2)

OnAuthorization method of AuthorizeAttribute的实施扫描AllowAnonymousAttribute。因此,您必须不覆盖此方法,或者如果您希望该部分工作,请重新执行此检查。由于您只提供了AuthorizeAttribute的简化实现,因此不能假设您没有覆盖此方法(从而覆盖了进行检查的逻辑)。

此外,您的示例控制器实际上并未显示AllowAnonymousAttribute 的使用情况。相反,它设置名为AllowAnonymous的属性。如果您希望匿名用户访问该操作方法,则应使用MVC实际扫描的属性对其进行修饰。

[AuthorizePublic(Sites = AuthSites.Corporate)]
public class CorporateController : SecuredController
{
    [AllowAnonymous]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View();
    }
}

或者,如果您需要以某种方式自定义AllowAnonymous行为,您可以继续使用您拥有的属性,但您必须自己实施Reflection代码以扫描AuthorizePublic并检查AllowAnonymous财产。

public class AuthorizePublic : AuthorizeAttribute
{
    public AuthSites Sites { get; set; }
    public bool AllowAnonymous { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var actionDescriptor = httpContext.Items["ActionDescriptor"] as ActionDescriptor;
        if (actionDescriptor != null)
        {
            AuthorizePublic attribute = GetAuthorizePublicAttribute(actionDescriptor);
            if (attribute.AllowAnonymous)
                return true;

            var sites = attribute.Sites;

            // Logic
        }
        return true;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Pass the current action descriptor to the AuthorizeCore
        // method on the same thread by using HttpContext.Items
        filterContext.HttpContext.Items["ActionDescriptor"] = filterContext.ActionDescriptor;
        base.OnAuthorization(filterContext);
    }

    // Gets the Attribute instance of this class from an action method or contoroller.
    // An action method will override a controller.
    private AuthorizePublic GetAuthorizePublicAttribute(ActionDescriptor actionDescriptor)
    {
        AuthorizePublic result = null;

        // Check if the attribute exists on the action method
        result = (AuthorizePublic)actionDescriptor
            .GetCustomAttributes(attributeType: typeof(AuthorizePublic), inherit: true)
            .SingleOrDefault();

        if (result != null)
        {
            return result;
        }

        // Check if the attribute exists on the controller
        result = (AuthorizePublic)actionDescriptor
            .ControllerDescriptor
            .GetCustomAttributes(attributeType: typeof(AuthorizePublic), inherit: true)
            .SingleOrDefault();

        return result;
    }
}

AuthorizeAttribute同时实现AttributeIAuthorizationFilter。考虑到这一点,IAuthorizationFilter的{​​{1}}部分是与<{1}}部分不同的类运行时实例。因此前者必须使用Reflection来读取后者的属性才能使其工作。您不能只从当前实例中读取AuthorizeAttribute属性并期望它可以工作,因为您正在设置属性中的值,并且代码正在过滤器中执行。

  

MVC和Web API是完全独立的框架,具有各自独立的配置,即使它们可以在同一个项目中共存。 MVC将完全忽略Web API中定义的任何控制器或属性,反之亦然。