Zastąp atrybut autoryzacji w ASP.NET MVC

83

Mam klasę bazową kontrolera MVC, na której zastosowałem atrybut Autoryzuj, ponieważ chcę, aby prawie wszystkie kontrolery (i ich akcje) były autoryzowane.

Jednak potrzebuję, aby administrator i działanie innego administratora były nieuprawnione. Chciałem móc je ozdobić [Authorize(false)]czy czymś, ale to nie jest dostępne.

Jakieś pomysły?

Andrei Rînea
źródło

Odpowiedzi:

101

Edycja: ponieważ ASP.NET MVC 4 najlepszym podejściem jest po prostu użycie wbudowanego atrybutu AllowAnonymous .

Poniższa odpowiedź dotyczy wcześniejszych wersji ASP.NET MVC

Można utworzyć niestandardowy atrybut autoryzacji dziedziczący po standardowym AuthorizeAttribute z opcjonalnym parametrem bool, aby określić, czy autoryzacja jest wymagana, czy nie.

public class OptionalAuthorizeAttribute : AuthorizeAttribute
{
    private readonly bool _authorize;

    public OptionalAuthorizeAttribute()
    {
        _authorize = true;
    }

    public OptionalAuthorizeAttribute(bool authorize)
    {
        _authorize = authorize;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if(!_authorize)
            return true;

                    return base.AuthorizeCore(httpContext);
    }
}

Następnie możesz ozdobić swój podstawowy kontroler tym atrybutem:

[OptionalAuthorize]
public class ControllerBase : Controller
{
}

a dla kontrolerów, których nie chcesz autoryzować, po prostu użyj zastąpienia z wartością „fałsz” - np

[OptionalAuthorize(false)]
public class TestController : ControllerBase
{
    public ActionResult Index()
    {
        return View();
    }
}
Steve Willcock
źródło
Myślałem o tym, ale liczyłem na prostsze rozwiązanie. Jeśli jednak „oni” go nie dostarczyli, Twoje rozwiązanie jest najlepsze.
Andrei Rînea,
2
Lepiej jest użyć [AllowAnonymous]atrybutu.
Jaider
Czekaj ... więc kontroler honoruje tylko atrybut klasy najwyższego poziomu określonego typu?
Triynko
Czy wiesz, dlaczego tak naprawdę LEPSZE jest użycie AllowAnonymous? Ponieważ masz lepszą kontrolę. W moim przypadku chcę wyłączyć punkty końcowe autoryzacji tylko dla niektórych środowisk, na przykład nie potrzebujesz tego dla localhost. Zapewnia to bardziej eleganckie rozwiązanie niż to, co zamierzałem zrobić w moim startup.cs. Rozmawiamy tutaj 11 lat później.
sksallaj
76

Wygląda na to, że ASP.NET MVC 4 „naprawił” ten problem, dodając atrybut AllowAnonymous .

David Hayden napisał o tym :

[Authorize]
public class AccountController : Controller
{
    [AllowAnonymous]
    public ActionResult Login()
    {
        // ...
    }

    // ...
}
Andrei Rînea
źródło
15

Moim osobistym podejściem do tego byłoby podzielenie kontrolera. Po prostu utwórz kolejny kontroler Do działań, których nie potrzebujesz uwierzytelniania.

Lub możesz mieć:

  • BaseController
    nie wymaga uwierzytelniania - tutaj masz wszystkie swoje "podstawowe rzeczy" :).

  • BaseAuthController : BaseController
    wszystkie działania tutaj wymagają uwierzytelnienia.

W ten sposób możesz mieć uwierzytelnianie, kiedy chcesz, po prostu poprzez wyprowadzenie z określonej klasy.

sirrocco
źródło
6

Jeśli chcesz, aby tylko jedna akcja była nieautoryzowana na autoryzowanym kontrolerze, możesz zrobić coś takiego:

public class RequiresAuthorizationAttribute : ActionFilterAttribute
{
    private readonly bool _authorize;

    public RequiresAuthorizationAttribute()
    {
        _authorize = true;
    }

    public RequiresAuthorizationAttribute(bool authorize)
    {
        _authorize = authorize;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var overridingAttributes = filterContext.ActionDescriptor.GetCustomAttributes(typeof (RequiresAuthorizationAttribute), false);

        if (overridingAttributes.Length > 0 && overridingAttributes[0] as RequiresAuthorizationAttribute != null && !((RequiresAuthorizationAttribute)overridingAttributes[0])._authorize)
            return;

        if (_authorize)
        {
            //redirect if not authenticated
            if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                //use the current url for the redirect
                var redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath;

                //send them off to the login page
                //var redirectUrl = string.Format("?RedirectUrl={0}", redirectOnSuccess);
                var loginUrl = LinkBuilder.BuildUrlFromExpression<HomeController>(filterContext.RequestContext, RouteTable.Routes,
                                                                                  x => x.Login(redirectOnSuccess));
                filterContext.HttpContext.Response.Redirect(loginUrl, true);
            }
        }
    }
}
stawermatyczny
źródło