2009-09-30 14 views
43

Nel mio ASP.NET MVC app, ho maggior parte dei controllori decorate conASP.NET MVC - Come mostrare un errore non autorizzato sulla pagina di accesso?

[Authorize(Roles="SomeGroup")] 

Quando un utente non è autorizzato ad accedere a qualcosa, sono inviati a "~/Login", che è l'azione di login sul mio account controller.

Come è possibile determinare se un utente ha raggiunto la pagina di accesso per non essere autorizzato in modo che possa mostrare un errore appropriato?

risposta

28

È possibile cercare il valore di querystring ?ReturnUrl= oppure è possibile creare il proprio filtro di autorizzazione & impostare un campo in TempData indicando il motivo.

Ecco un semplice filtro personalizzato che farà il trucco:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 

    // NOTE: This is not thread safe, it is much better to store this 
    // value in HttpContext.Items. See Ben Cull's answer below for an example. 
    private bool _isAuthorized; 

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) 
    { 
     _isAuthorized = base.AuthorizeCore(httpContext); 
     return _isAuthorized; 
    } 

    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     if(!_isAuthorized) 
     { 
      filterContext.Controller.TempData.Add("RedirectReason", "Unauthorized"); 
     } 
    } 
} 

Poi a suo avviso, si può fare qualcosa di simile:

@if(TempData["RedirectReason"] == "Unauthorized") 
{ 
    <b>You don't have permission to access that area</b> 
} 

(Anche se mi consiglia un approccio migliore di queste stringhe magiche, ma si ottiene il punto)

+0

facile da implementare e funziona. Grazie. –

+0

Grazie Ben, realizzo lo stesso dalla mia parte e lavoro molto bene! –

+0

Questa risposta è accettata, ma non è thread-safe, vedere la risposta sotto per maggiori dettagli. Ti preghiamo di aggiornare il tuo codice per essere sicuro, molti sviluppatori potrebbero non leggere altre risposte e usare il tuo come il migliore. –

74

UPDATE (giugno 2015): @ daniel-lidström ha correttamente rilevato che non è necessario utilizzare Response.Redirect in un'applicazione ASP.NET MVC. Per ulteriori informazioni sul motivo, vedere questo link: Response.Redirect and ASP.NET MVC – Do Not Mix.

UPDATE (settembre 2014): non sono sicuro quando HandleUnauthorizedRequest è stato aggiunto al AuthorizeAttribute, ma in ogni modo sono stato in grado di perfezionare il codice AuthorizeRedirect in qualcosa di più piccolo e più semplice.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class AuthorizeRedirect : AuthorizeAttribute 
{ 
    public string RedirectUrl = "~/Error/Unauthorized"; 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 

     if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      filterContext.Result = new RedirectResult(RedirectUrl); 
     } 
    } 
} 

Risposta originale Sotto (ancora perfettamente funzionante)

Ho lasciato questa risposta qui come ti dà ancora una comprensione di come le opere di autorizzazione di pipeline.

Per chi arriva ancora qui, ho modificato la risposta di Ben Scheirman al reindirizzamento automatico a una pagina non autorizzata quando l'utente ha effettuato l'accesso ma non è autorizzato. È possibile modificare il percorso di reindirizzamento utilizzando il parametro name RedirectUrl.

EDIT: Ho fatto i thread-safe soluzione grazie ai consigli di Tarynn e MSDN

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class AuthorizeRedirect : AuthorizeAttribute 
{ 
    private const string IS_AUTHORIZED = "isAuthorized"; 

    public string RedirectUrl = "~/error/unauthorized"; 

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) 
    { 
     bool isAuthorized = base.AuthorizeCore(httpContext); 

     httpContext.Items.Add(IS_AUTHORIZED, isAuthorized); 

     return isAuthorized; 
    } 

    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     var isAuthorized = filterContext.HttpContext.Items[IS_AUTHORIZED] != null 
      ? Convert.ToBoolean(filterContext.HttpContext.Items[IS_AUTHORIZED]) 
      : false; 

     if (!isAuthorized && filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl); 
     } 
    } 
} 
+5

Da MSDN: derivanti da AuthorizeAttribute Se si deriva dalla classe AuthorizeAttribute, il tipo derivato deve essere thread-safe. Pertanto, non memorizzare lo stato in un'istanza del tipo stesso (ad esempio, in un campo dell'istanza) a meno che tale stato non si intenda applicare a tutte le richieste. Invece, memorizza lo stato per richiesta nella proprietà Items, accessibile tramite gli oggetti di contesto passati a AuthorizeAttribute. non è un campo di istanza _is Autorizzato? – Tarynn

+0

Wow buona cattura, aggiornerò la soluzione sopra per abbinare. –

+0

Questo metodo funziona con FormsAuth e il reindirizzamento predefinito? –

4

Se si dispone di un controller e non si vuole avere un URL in voi codice puoi reindirizzare anche in questo modo. Non cambierà l'url nella barra degli indirizzi del browser, quindi l'utente non vedrà mai l'url per la pagina non autorizzata. Questo è stato scritto in MVC 3. Questo metodo funzionerà anche se desideri reindirli a una pagina di accesso o se desideri reindirli a una pagina per dire loro che non sono autorizzati. Avevo sezione nel programma che alcuni utenti non avevano i diritti ma erano loggati quindi questo è quello che ho usato.

public class AuthorizedRedirect : AuthorizeAttribute 
{ 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     bool isAuthorized = base.AuthorizeCore(httpContext); 
     return isAuthorized; 
    } 
protect override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
{ 
    filterContext.RequestContext.RouteData.Values["controller"] = "error"; 
    filterContext.Result = new ViewResult { ViewName = "unauthorized" }; 
} 
+0

Come un'altra variante per mostrare il messaggio in una vista condivisa, ad es. 'var vr = new ViewResult(); vr.ViewName = "Informazioni"; vr.ViewBag.Message = "Non sei autorizzato per questa pagina, contattaci."; filterContext.Result = vr; ' – subsci

2

E una versione ancora più semplice che utilizza le impostazioni di FormsAuthentication. Per chi non ha familiarità con Contract, Contract.Requires è un'aggiunta .NET 4. Pro e contro nell'uso di Code Contracts.

public class RequiresAttribute : AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     Contract.Requires(filterContext != null); 

     HttpContextBase context = filterContext.RequestContext.HttpContext; 

     if (context.User.Identity.IsAuthenticated) 
     { 
      // user does not possess the required role permission 
      string url = context.GetCustomErrorUrl(401); 
      context.Response.Redirect(url); 
     } 
     else 
     { 

      // redirect the user to the login page 
      string extraQueryString = context.Request.RawUrl; 
      FormsAuthentication.RedirectToLoginPage(extraQueryString); 
     } 
    } 
} 
1

Andando più lontano dalla risposta di divide_byzero anche se non si dispone di un controller è comunque possibile utilizzare la HandleUnauthorizedRequest per cambiare il reindirizzamento.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
    public class AuthoriseRedirect : AuthorizeAttribute 
    { 
     protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
     { 
      filterContext.RequestContext.HttpContext.Response.Redirect("UrlToRedirectTo"); 
     } 
    } 

È utile se si dispone di un sito webforms eredità che vi sarà la conversione in MVC per un periodo di tempo più lungo .....!

4

Il metodo di Ben Cull funziona bene, ma ricorda che esistono due classi AuthorizeAttribute: una in System.Web.HTTP (utilizzata da Web API) e l'altra in System.Web.Mvc. Il metodo di Ben utilizza la classe System.Web.Mvc. Per chiarezza, suggerisco di utilizzare il percorso completo.

Se si utilizza API Web insieme a MVC, è necessario implementare due filtri:

public class AuthorizeRedirectMVCAttribute : System.Web.Mvc.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 

     if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      filterContext.Result = new RedirectResult("~/Account/AccessDenied"); 
     } 
    } 
} 

public class AuthorizeRedirectAPIAttribute : System.Web.Http.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) 
    { 
     base.HandleUnauthorizedRequest(actionContext); 

     if (actionContext.RequestContext.Principal.Identity.IsAuthenticated) 
     { 
      actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden); 
     } 
    } 
} 

Nota che asp.net vi permetterà di decorare il controller MVC con un filtro API - è appena vinto' funziona come ti aspetti, quindi tieni i nomi degli attributi espliciti.

0

Mi piace quello che ha scritto Brian Vander Plaats, appena aggiunto alcuni miglioramenti:

/// <summary> 
/// Authorize or redirect to an unauthorized MVC action if the user does not have the required roles 
/// (an unauthenticated user will be redirected to the defualt sign in action) 
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para> 
/// </summary> 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public class AuthorizeOrRedirectAttribute : System.Web.Mvc.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 

     if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      var routeData = new RouteData(); 
      routeData.Values.Add("controller", "Error"); 
      routeData.Values.Add("action", "Unauthorized"); 
      filterContext.Result = new RedirectToRouteResult(routeData.Values); 
     } 
    } 
} 

/// <summary> 
/// Authorize or redirect to an unauthorized API action if the user does not have the required roles 
/// (an unauthenticated user will be redirected to the defualt sign in action) 
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para> 
/// </summary> 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public class AuthorizeOrRedirectApiFilterAttribute : System.Web.Http.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) 
    { 
     base.HandleUnauthorizedRequest(actionContext); 

     if (actionContext.RequestContext.Principal.Identity.IsAuthenticated) 
     { 
      actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); 
     } 
    } 
} 
Problemi correlati