Authentifizierung und Autorisierung in der ASP.NET Web-API

von Rick Anderson

Sie haben eine Web-API erstellt, aber jetzt möchten Sie den Zugriff darauf steuern. In dieser Artikelreihe werden einige Optionen zum Schützen einer Web-API vor nicht autorisierten Benutzern erläutert. Diese Reihe behandelt sowohl die Authentifizierung als auch die Autorisierung.

  • Bei der Authentifizierung wird die Identität des Benutzers bekannt. Beispielsweise meldet sich Alice mit ihrem Benutzernamen und Kennwort an, und der Server verwendet das Kennwort, um Alice zu authentifizieren.
  • Bei der Autorisierung wird entschieden, ob ein Benutzer eine Aktion ausführen darf. Beispielsweise verfügt Alice über die Berechtigung, eine Ressource abzurufen, aber keine Ressource zu erstellen.

Der erste Artikel der Reihe bietet einen allgemeinen Überblick über die Authentifizierung und Autorisierung in ASP.NET-Web-API. In anderen Themen werden allgemeine Authentifizierungsszenarien für die Web-API beschrieben.

Hinweis

Vielen Dank an die Personen, die diese Serie überprüft haben und wertvolles Feedback gegeben haben: Rick Anderson, Levi Broderick, Barry Dorrans, Tom Dykstra, Hongmei Ge, David Matson, Daniel Roth, Tim Teebken.

Authentifizierung

Die Web-API geht davon aus, dass die Authentifizierung auf dem Host erfolgt. Beim Webhosting ist der Host IIS, das HTTP-Module für die Authentifizierung verwendet. Sie können Ihr Projekt so konfigurieren, dass eines der in IIS oder ASP.NET integrierten Authentifizierungsmodule verwendet wird, oder Ein eigenes HTTP-Modul schreiben, um eine benutzerdefinierte Authentifizierung durchzuführen.

Wenn der Host den Benutzer authentifiziert, erstellt er einen Prinzipal, bei dem es sich um ein IPrincipal-Objekt handelt, das den Sicherheitskontext darstellt, unter dem Code ausgeführt wird. Der Host fügt den Prinzipal durch Festlegen von Thread.CurrentPrincipal an den aktuellen Thread an. Der Prinzipal enthält ein zugeordnetes Identity-Objekt , das Informationen über den Benutzer enthält. Wenn der Benutzer authentifiziert ist, gibt die Identity.IsAuthenticated-Eigenschafttrue zurück. Für anonyme Anforderungen gibt IsAuthenticatedfalse zurück. Weitere Informationen zu Prinzipalen finden Sie unter Rollenbasierte Sicherheit.

HTTP-Nachrichtenhandler für die Authentifizierung

Anstatt den Host für die Authentifizierung zu verwenden, können Sie Authentifizierungslogik in einen HTTP-Nachrichtenhandler einfügen. In diesem Fall untersucht der Nachrichtenhandler die HTTP-Anforderung und legt den Prinzipal fest.

Wann sollten Sie Nachrichtenhandler für die Authentifizierung verwenden? Hier sind einige Kompromisse:

  • Ein HTTP-Modul sieht alle Anforderungen, die die ASP.NET-Pipeline durchlaufen. Ein Nachrichtenhandler sieht nur Anforderungen, die an die Web-API weitergeleitet werden.
  • Sie können Nachrichtenhandler pro Route festlegen, sodass Sie ein Authentifizierungsschema auf eine bestimmte Route anwenden können.
  • HTTP-Module sind spezifisch für IIS. Nachrichtenhandler sind hostunabhängig, sodass sie sowohl mit Webhosting als auch mit Selbsthosting verwendet werden können.
  • HTTP-Module sind an der IIS-Protokollierung, -Überwachung usw. beteiligt.
  • HTTP-Module werden früher in der Pipeline ausgeführt. Wenn Sie die Authentifizierung in einem Nachrichtenhandler verarbeiten, wird der Prinzipal erst festgelegt, wenn der Handler ausgeführt wird. Darüber hinaus wird der Prinzipal wieder auf den vorherigen Prinzipal zurückgesetzt, wenn die Antwort den Nachrichtenhandler verlässt.

Wenn Sie selfhosting nicht unterstützen müssen, ist ein HTTP-Modul im Allgemeinen die bessere Option. Wenn Sie selfhosting unterstützen müssen, sollten Sie einen Nachrichtenhandler in Betracht ziehen.

Festlegen des Prinzipals

Wenn Ihre Anwendung eine benutzerdefinierte Authentifizierungslogik ausführt, müssen Sie den Prinzipal an zwei Stellen festlegen:

  • Thread.CurrentPrincipal. Diese Eigenschaft ist die Standardmethode zum Festlegen des Prinzipals des Threads in .NET.
  • HttpContext.Current.User. Diese Eigenschaft ist spezifisch für ASP.NET.

Der folgende Code zeigt, wie der Prinzipal festgelegt wird:

private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

Beim Webhosting müssen Sie den Prinzipal an beiden Stellen festlegen. Andernfalls kann der Sicherheitskontext inkonsistent werden. Für das Selbsthosting ist HttpContext.Current jedoch NULL. Um sicherzustellen, dass Ihr Code hostunabhängig ist, überprüfen Sie daher auf NULL, bevor Sie httpContext.Current zuweisen, wie gezeigt.

Authorization

Die Autorisierung erfolgt später in der Pipeline, näher am Controller. Auf diese Weise können Sie präzisere Entscheidungen treffen, wenn Sie Zugriff auf Ressourcen gewähren.

  • Autorisierungsfilter werden vor der Controlleraktion ausgeführt. Wenn die Anforderung nicht autorisiert ist, gibt der Filter eine Fehlerantwort zurück, und die Aktion wird nicht aufgerufen.
  • Innerhalb einer Controlleraktion können Sie den aktuellen Prinzipal aus der ApiController.User-Eigenschaft abrufen. Beispielsweise können Sie eine Liste von Ressourcen basierend auf dem Benutzernamen filtern und nur die Ressourcen zurückgeben, die zu diesem Benutzer gehören.

Diagramm der Authentifizierungs- und Autorisierungspipeline.

Verwenden des Attributs [Authorize]

Die Web-API bietet den integrierten Autorisierungsfilter AuthorizeAttribute. Dieser Filter überprüft, ob der Benutzer authentifiziert ist. Andernfalls wird HTTP-status Code 401 (Nicht autorisiert) ohne Aufrufen der Aktion zurückgegeben.

Sie können den Filter global, auf Controllerebene oder auf der Ebene einzelner Aktionen anwenden.

Global: Um den Zugriff für jeden Web-API-Controller einzuschränken, fügen Sie den AuthorizeAttribute-Filter der globalen Filterliste hinzu:

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

Controller: Um den Zugriff für einen bestimmten Controller einzuschränken, fügen Sie dem Controller den Filter als Attribut hinzu:

// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
    public HttpResponseMessage Get(int id) { ... }
    public HttpResponseMessage Post() { ... }
}

Aktion: Fügen Sie der Aktionsmethode das Attribut hinzu, um den Zugriff für bestimmte Aktionen einzuschränken:

public class ValuesController : ApiController
{
    public HttpResponseMessage Get() { ... }

    // Require authorization for a specific action.
    [Authorize]
    public HttpResponseMessage Post() { ... }
}

Alternativ können Sie den Controller einschränken und dann den anonymen Zugriff auf bestimmte Aktionen zulassen, indem Sie das [AllowAnonymous] -Attribut verwenden. Im folgenden Beispiel ist die Post -Methode eingeschränkt, aber die Get -Methode lässt anonymen Zugriff zu.

[Authorize]
public class ValuesController : ApiController
{
    [AllowAnonymous]
    public HttpResponseMessage Get() { ... }

    public HttpResponseMessage Post() { ... }
}

In den vorherigen Beispielen ermöglicht der Filter jedem authentifizierten Benutzer den Zugriff auf die eingeschränkten Methoden. nur anonyme Benutzer werden ferngehalten. Sie können den Zugriff auch auf bestimmte Benutzer oder Benutzer in bestimmten Rollen beschränken:

// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
   
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}

Hinweis

Der AuthorizeAttribute-Filter für Web-API-Controller befindet sich im System.Web.Http-Namespace . Es gibt einen ähnlichen Filter für MVC-Controller im System.Web.Mvc-Namespace , der nicht mit Web-API-Controllern kompatibel ist.

Benutzerdefinierte Autorisierungsfilter

Um einen benutzerdefinierten Autorisierungsfilter zu schreiben, leiten Sie von einem der folgenden Typen ab:

  • AuthorizeAttribute. Erweitern Sie diese Klasse, um Autorisierungslogik basierend auf dem aktuellen Benutzer und den Rollen des Benutzers auszuführen.
  • AuthorizationFilterAttribute. Erweitern Sie diese Klasse, um synchrone Autorisierungslogik auszuführen, die nicht unbedingt auf dem aktuellen Benutzer oder der aktuellen Rolle basiert.
  • IAuthorizationFilter. Implementieren Sie diese Schnittstelle, um asynchrone Autorisierungslogik auszuführen. beispielsweise, wenn Ihre Autorisierungslogik asynchrone E/A- oder Netzwerkaufrufe durchführt. (Wenn Ihre Autorisierungslogik CPU-gebunden ist, ist es einfacher, von AuthorizationFilterAttribute abzuleiten, da Sie dann keine asynchrone Methode schreiben müssen.)

Das folgende Diagramm zeigt die Klassenhierarchie für die AuthorizeAttribute-Klasse .

Diagramm der Klassenhierarchie für die Authorize Attribute-Klasse.

Diagramm der Klassenhierarchie für die Authorize Attribute-Klasse. Autorisieren-Attribut befindet sich unten, mit einem Pfeil, der auf das Autorisierungsfilter-Attribut zeigt, und ein Pfeil, der auf I-Autorisierungsfilter am oberen Rand zeigt.

Autorisierung innerhalb einer Controlleraktion

In einigen Fällen können Sie zulassen, dass eine Anforderung fortgesetzt wird, aber das Verhalten basierend auf dem Prinzipal ändern. Beispielsweise können sich die von Ihnen zurückgegebenen Informationen abhängig von der Rolle des Benutzers ändern. Innerhalb einer Controllermethode können Sie den aktuellen Prinzipal aus der ApiController.User-Eigenschaft abrufen.

public HttpResponseMessage Get()
{
    if (User.IsInRole("Administrators"))
    {
        // ...
    }
}