Perché AuthorizeAttribute reindirizza alla pagina di accesso per l’autenticazione e gli errori di authorization?

In ASP.NET MVC, puoi contrassegnare un metodo di controllo con AuthorizeAttribute , come questo:

 [Authorize(Roles = "CanDeleteTags")] public void Delete(string tagName) { // ... } 

Ciò significa che, se l’utente attualmente loggato non si trova nel ruolo “CanDeleteTags”, il metodo del controller non verrà mai chiamato.

Sfortunatamente, per gli errori, AuthorizeAttribute restituisce HttpUnauthorizedResult , che restituisce sempre il codice di stato HTTP 401. Ciò causa un reindirizzamento alla pagina di accesso.

Se l’utente non ha effettuato l’accesso, questo ha perfettamente senso. Tuttavia, se l’utente ha già effettuato l’accesso, ma non ha il ruolo richiesto, è difficile confonderlo con la pagina di accesso.

Sembra che AuthorizeAttribute confluisca con l’autenticazione e l’authorization.

    Questo sembra un po ‘di supervisione in ASP.NET MVC, o mi manca qualcosa?

    Ho dovuto preparare un DemandRoleAttribute che separa i due. Quando l’utente non è autenticato, restituisce l’HTTP 401, inviandolo alla pagina di accesso. Quando l’utente ha effettuato l’accesso, ma non ha il ruolo richiesto, crea invece un NotAuthorizedResult . Attualmente questo reindirizza a una pagina di errore.

    Sicuramente non dovevo farlo?

    Quando è stato sviluppato, System.Web.Mvc.AuthorizeAttribute stava facendo la cosa giusta: revisioni precedenti delle specifiche HTTP utilizzavano il codice di stato 401 sia per “non autorizzati” che per “non autenticati”.

    Dalla specifica originale:

    Se la richiesta includeva già le credenziali di authorization, la risposta 401 indica che l’authorization è stata rifiutata per tali credenziali.

    In effetti, puoi vedere la confusione proprio lì – usa la parola “authorization” quando significa “autenticazione”. Nella pratica quotidiana, tuttavia, ha più senso restituire un 403 Proibito quando l’utente è autenticato ma non autorizzato. È improbabile che l’utente disponga di un secondo set di credenziali che gli consentirebbe l’accesso: un’esperienza utente negativa in tutto ciò.

    Considerare la maggior parte dei sistemi operativi: quando si tenta di leggere un file a cui non si ha il permesso di accedere, non viene visualizzata una schermata di accesso!

    Fortunatamente, le specifiche HTTP sono state aggiornate (giugno 2014) per rimuovere l’ambiguità.

    Da “Protocollo di trasporto Hyper Text (HTTP / 1.1): autenticazione” (RFC 7235):

    Il codice di stato 401 (Non autorizzato) indica che la richiesta non è stata applicata perché non dispone di credenziali di autenticazione valide per la risorsa di destinazione.

    Da “Hypertext Transfer Protocol (HTTP / 1.1): semantica e contenuto” (RFC 7231):

    Il codice di stato 403 (Proibito) indica che il server ha compreso la richiesta ma si rifiuta di autorizzarlo.

    È interessante notare che, al momento della pubblicazione di ASP.NET MVC 1, il comportamento di AuthorizeAttribute era corretto. Ora, il comportamento non è corretto: la specifica HTTP / 1.1 è stata corretta.

    Anziché tentare di modificare i reindirizzamenti della pagina di accesso di ASP.NET, è più semplice correggere il problema all’origine. Puoi creare un nuovo attributo con lo stesso nome ( AuthorizeAttribute ) nello spazio dei nomi predefinito del tuo sito Web (questo è molto importante), quindi il compilatore lo raccoglierà automaticamente anziché quello standard di MVC. Ovviamente, potresti sempre dare un nuovo nome all’attributo se preferisci prendere questo approccio.

     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute { protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAuthenticated) { filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden); } else { base.HandleUnauthorizedRequest(filterContext); } } } 

    Aggiungi questo alla tua funzione Page_Load di accesso:

     // User was redirected here because of authorization section if (User.Identity != null && User.Identity.IsAuthenticated) Response.Redirect("Unauthorized.aspx"); 

    Quando l’utente viene reindirizzato ma è già connesso, mostra la pagina non autorizzata. Se non sono connessi, cade e mostra la pagina di accesso.

    Ho sempre pensato che questo avesse senso. Se sei loggato e cerchi di colpire una pagina che richiede un ruolo che non hai, ti viene inoltrata alla schermata di accesso che ti chiede di accedere con un utente che ha il ruolo.

    È ansible aggiungere la logica alla pagina di accesso che verifica se l’utente è già autenticato. Potresti aggiungere un messaggio amichevole che spiega il motivo per cui sono stati riportati di nuovo lì.

    Sfortunatamente, hai a che fare con il comportamento predefinito dell’autenticazione dei moduli ASP.NET. C’è una soluzione alternativa (non l’ho ancora provata) discussa qui:

    http://www.codeproject.com/KB/aspnet/Custon401Page.aspx

    (Non è specifico per MVC)

    Penso che nella maggior parte dei casi la soluzione migliore sia limitare l’accesso a risorse non autorizzate prima che l’utente provi ad arrivarci. Rimuovendo / oscurando il collegamento o il pulsante che potrebbe portarli a questa pagina non autorizzata.

    Probabilmente sarebbe bello avere un parametro aggiuntivo sull’attributo per specificare dove redirect un utente non autorizzato. Ma nel frattempo, considero AuthorizeAttribute come una rete di sicurezza.

    Prova questo nel tuo nel gestore Application_EndRequest del tuo file Global.ascx

     if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("//")) { HttpContext.Current.Response.ClearContent(); Response.Redirect("~/AccessDenied.aspx"); } 

    Se usi aspnetcore 2.0, usa questo:

     using System; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Core { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class AuthorizeApiAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { var user = context.HttpContext.User; if (!user.Identity.IsAuthenticated) { context.Result = new UnauthorizedResult(); return; } } } }