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

Sav*_*age 0 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

以及属性:

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

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        // Logic
    }        
}
Run Code Online (Sandbox Code Playgroud)

作为最后的手段,我可​​以将登录操作移动到它们自己的控制器上,但在这样做之前,我是否缺少一些东西来使这些方法之一发挥作用?我有点惊讶的是操作级属性并没有覆盖控制器级属性。

Nig*_*888 5

它是扫描OnAuthorization的方法 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您需要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;
    }
}
Run Code Online (Sandbox Code Playgroud)

AuthorizeAttribute实现AttributeIAuthorizationFilter。考虑到这一点,IAuthorizationFilter的部分AuthorizeAttribute类的Attribute部分不同的运行时实例。所以前者必须使用反射来读取后者的属性才能工作。您不能只AllowAnonymous从当前实例中读取属性并期望它起作用,因为您正在属性中设置值并且代码正在过滤器中执行。

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