简体   繁体   English

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

[英]Get MVC AllowAnonymous to override custom authorize attribute

I have created a custom authorize attribute, but I need some actions to allow anonymous access. 我已经创建了一个自定义授权属性,但是我需要一些操作来允许匿名访问。 I've tried three different approaches without success: use AllowAnonymous , update the existing attribute with additional parameters, and create a new overriding attribute. 我尝试了三种不同的方法,但AllowAnonymous成功:使用AllowAnonymous ,使用其他参数更新现有属性以及创建新的覆盖属性。 Basically it seems that the controller-level attribute always gets called before the action-level attribute. 基本上,似乎总是在操作级属性之前调用控制器级属性。

Here's the controller: 这是控制器:

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

And the attribute: 和属性:

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

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

As a last resort I can move the login actions onto their own controller, but before I do that, am I missing something to get one of these approaches to work? 作为最后的选择,我可以将登录操作移至其自己的控制器上,但是在此之前,我是否缺少使这些方法之一起作用的东西? I'm a bit surprised that action-level attributes aren't overriding controller-level attributes. 我对动作级属性没有覆盖控制器级属性感到惊讶。

It is the implementation of the OnAuthorization method of AuthorizeAttribute that scans for AllowAnonymousAttribute . AuthorizeAttributeOnAuthorization方法的实现将扫描AllowAnonymousAttribute So, you must either not override this method or re-implement this check if you want that part to work. 因此,如果您希望该部分正常工作,则不能覆盖此方法或重新实施此检查。 Since you have only provided a cut-down implementation of AuthorizeAttribute , it cannot be assumed that you are not overriding this method (and thus overriding the logic that makes the check). 由于您仅提供了AuthorizeAttribute的简化实现,因此不能假定您没有覆盖此方法(因此也没有覆盖进行检查的逻辑)。

Also, your example controller doesn't actually show usage of the AllowAnonymousAttribute . 同样,您的示例控制器实际上并未显示AllowAnonymousAttribute用法 Instead, it sets a property named AllowAnonymous . 而是设置一个名为AllowAnonymous的属性。 If you expect anonymous users to reach that action method, you should decorate it with the attribute that MVC is actually scanning for. 如果希望匿名用户使用该操作方法,则应使用MVC实际扫描的属性来装饰它。

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

Alternatively, if you need to customize the AllowAnonymous behavior in some way, you can keep using the property you have, but you have to implement the Reflection code yourself to scan for AuthorizePublic and check the AllowAnonymous property. 另外,如果您需要以某种方式自定义AllowAnonymous行为,则可以继续使用拥有的属性,但是必须自己实现反射代码以扫描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 implements both Attribute and IAuthorizationFilter . AuthorizeAttribute实现AttributeIAuthorizationFilter With that in mind, the IAuthorizationFilter part of AuthorizeAttribute is a different runtime instance of the class than the Attribute part. 考虑到这一点, AuthorizeAttributeIAuthorizationFilter部分与Attribute部分是类不同运行时实例 So the former must use Reflection to read the property of the latter in order for it to work. 因此,前者必须使用反射来读取后者的属性,以使其起作用。 You can't just read the AllowAnonymous property from the current instance and expect it to work, because you are setting the value in the attribute and the code is executing in the filter. 您不能只是从当前实例中读取AllowAnonymous属性并期望它起作用,因为您正在属性中设置值,并且代码正在过滤器中执行。

MVC and Web API are completely separate frameworks with their own separate configuration even though they can co-exist in the same project. MVC和Web API是完全独立的框架,具有各自独立的配置,即使它们可以共存于同一项目中。 MVC will completely ignore any controllers or attributes defined in Web API and vise versa. MVC将完全忽略Web API中定义的任何控制器或属性,反之亦然。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM