Warum AuthorizeAttribute auf die Anmeldeseite für die Authentifizierung und Autorisierung Ausfälle umleiten?

StackOverflow https://stackoverflow.com/questions/238437

Frage

In ASP.NET MVC können Sie eine Controller-Methode mit AuthorizeAttribute markieren, wie folgt aus:

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

Das bedeutet, dass, wenn der aktuell angemeldete Benutzer nicht in der „CanDeleteTags“ Rolle ist, wird die Controller-Methode nie aufgerufen werden.

Leider für Ausfälle, kehrt AuthorizeAttribute HttpUnauthorizedResult, die immer HTTP-Statuscode zurückgibt 401. Dies bewirkt eine Umleitung auf die Login-Seite.

Wenn der Benutzer nicht angemeldet ist, macht dies durchaus Sinn. Wenn jedoch der Benutzer bereits angemeldet, aber noch nicht in der gewünschten Rolle, es ist verwirrend sie zurück auf die Login-Seite zu senden.

Es scheint, dass AuthorizeAttribute verschmilzt Authentifizierung und Autorisierung.

Dies scheint ein bisschen ein Versehen in ASP.NET MVC, oder bin ich etwas fehlt?

Ich habe eine DemandRoleAttribute zu kochen, die die beiden trennt. Wenn der Benutzer nicht authentifiziert wird, gibt es HTTP 401, so dass sie auf die Login-Seite zu senden. Wenn der Benutzer angemeldet ist, ist aber nicht in der erforderlichen Rolle, erstellt es eine NotAuthorizedResult statt. Derzeit leitet dies auf eine Fehlerseite.

Sicher habe ich nicht, dies zu tun?

War es hilfreich?

Lösung

Als es zuerst entwickelt wurde, tat System.Web.Mvc.AuthorizeAttribute das Richtige - ältere Revisionen der HTTP-Spezifikation verwendet Statuscode 401 für beide „nicht autorisiert“ und „Nicht authentifiziert“.

Von der ursprünglichen Spezifikation:

  

Wenn die Anforderung bereits Berechtigungsnachweise enthalten, dann ist die 401-Antwort zeigt an, dass eine Genehmigung wurde für die Anmeldeinformationen verweigert.

In der Tat kann man die Verwirrung sieht genau dort - es verwendet das Wort „Ermächtigung“, wenn es bedeutet, „Authentifizierung“. In der täglichen Praxis jedoch macht es mehr Sinn, ein 403 Forbidden zurückzukehren, wenn der Benutzer authentifiziert ist, aber nicht autorisiert. Es ist unwahrscheinlich, dass der Benutzer einen zweiten Satz von Anmeldeinformationen haben würde, die ihnen den Zugang geben würde - schlechte Benutzererfahrung rundum

.

Die meisten Betriebssysteme Betrachten Sie - wenn Sie eine Datei zu lesen, versuchen Sie haben keine Berechtigung zum Zugriff, Sie sind nicht eine Login-Bildschirm angezeigt!

Zum Glück wurden die HTTP-Spezifikationen aktualisiert (Juni 2014), die Mehrdeutigkeit zu entfernen.

Von "Hyper Text Transport Protocol (HTTP / 1.1): Authentication" (RFC 7235):

  

Der 401 (Unauthorized) Statuscode gibt an, dass die Anforderung nicht angewandt worden ist, weil sie gültige Anmeldeinformationen für die Zielressource fehlen.

Von "Hypertext Transfer Protocol (HTTP / 1.1): Semantik und Content" (RFC 7231):

  

Die 403 (Verboten) Statuscode gibt an, dass der Server die Anforderung verstanden, aber sich weigert, sie zu genehmigen.

Interessanterweise zu der Zeit 1 ASP.NET MVC das Verhalten von AuthorizeAttribute veröffentlicht wurde, war korrekt. Nun wird das Verhalten ist falsch -. Die HTTP / 1.1-Spezifikation festgelegt wurde

Anstatt versuchen, ASP.NET-Login-Seite umleitet zu ändern, ist es einfacher, nur das Problem an der Quelle zu beheben. Sie können ein neues Attribut mit dem gleichen Namen erstellen (AuthorizeAttribute) in Standard-Namespace ist Ihre Website (das ist sehr wichtig), dann wird der Compiler automatisch statt MVC-Standard eines abholen. Natürlich könnte man immer das Attribut einen neuen Namen geben, wenn Sie lieber diesen Ansatz nehmen würde.

[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);
        }
    }
}

Andere Tipps

Fügen Sie diese auf Ihre Login-Page Load Funktion:

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

Wenn der Benutzer dort umgeleitet wird, sondern bereits angemeldet, zeigt es die nicht autorisierte Seite. Wenn sie nicht eingeloggt sind, es fällt durch und zeigt die Login-Seite.

Ich dachte immer, diese sinnvoll tat. Wenn Sie angemeldet sind und Sie versuchen, eine Seite zu schlagen, die eine Rolle erfordert, dass Sie nicht haben, Sie zum Anmeldebildschirm weitergeleitet bekommen fragen Sie mit einem Benutzer anmelden, der die Rolle hat.

Sie können Logik zur Login-Seite hinzufügen, wenn der Benutzer überprüft, ist bereits authentifiziert. Sie könnten eine freundliche Nachricht hinzufügen, die erklärt, warum sie wieder dort bumbed worden sind.

Leider sind Sie mit dem Standardverhalten von ASP.NET Formularauthentifizierung zu tun. Es gibt eine Abhilfe (ich habe es nicht ausprobiert) diskutiert hier:

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

(Es ist nicht spezifisch für MVC)

Ich glaube, in den meisten Fällen die beste Lösung Zugriff auf nicht autorisierte Ressourcen vor dem Benutzer zu beschränken, die versucht, dorthin zu gelangen. Durch das Entfernen / Vergrauung den Link oder eine Schaltfläche aus, die sie auf diese nicht autorisierte Seite nehmen könnten.

Wahrscheinlich wäre schön, einen zusätzlichen Parameter auf dem Attribut müssen angeben, wo ein nicht autorisierter Benutzer umleiten. Aber in der Zwischenzeit sehe ich die AuthorizeAttribute als Sicherheitsnetz.

Versuchen Sie dies in Ihrem im Application_EndRequest Handler Ihre Global.ascx Datei

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

Wenn Ihr aspnetcore 2.0 verwenden, verwenden Sie diese:

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;
            }
        }
    }
}

In meinem Fall war das Problem „HTTP-Spezifikation verwendet Statuscode 401 für beide‚nicht autorisiert‘und‚Nicht authentifiziert‘“. Wie shadowchaser sagte.

Diese Lösung funktioniert für mich:

if (User != null &&  User.Identity.IsAuthenticated && Response.StatusCode == 401)
{
    //Do whatever

    //In my case redirect to error page
    Response.RedirectToRoute("Default", new { controller = "Home", action = "ErrorUnauthorized" });
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top