Authentifizierung und Autorisierung für SignalR-Hubs

von Patrick Fletcher, Tom FitzMacken

Warnung

Diese Dokumentation gilt nicht für die neueste Version von SignalR. Sehen Sie sich ASP.NET Core SignalR an.

In diesem Thema wird beschrieben, wie Sie einschränken, welche Benutzer oder Rollen auf Hubmethoden zugreifen können.

In diesem Thema verwendete Softwareversionen

Frühere Versionen dieses Themas

Informationen zu früheren Versionen von SignalR finden Sie unter Ältere Versionen von SignalR.

Fragen und Kommentare

Bitte hinterlassen Sie Feedback dazu, wie Ihnen dieses Tutorial gefallen hat und was wir in den Kommentaren unten auf der Seite verbessern könnten. Wenn Sie Fragen haben, die nicht direkt mit dem Tutorial zusammenhängen, können Sie diese im ASP.NET SignalR-Forum oder StackOverflow.com posten.

Überblick

Dieses Thema enthält folgende Abschnitte:

Attribut autorisieren

SignalR stellt das Authorize-Attribut bereit, um anzugeben, welche Benutzer oder Rollen Zugriff auf einen Hub oder eine Methode haben. Dieses Attribut befindet sich im Microsoft.AspNet.SignalR Namespace. Sie wenden das Authorize Attribut entweder auf einen Hub oder auf bestimmte Methoden in einem Hub an. Wenn Sie das Authorize Attribut auf eine Hubklasse anwenden, wird die angegebene Autorisierungsanforderung auf alle Methoden im Hub angewendet. Dieses Thema enthält Beispiele für die verschiedenen Arten von Autorisierungsanforderungen, die Sie anwenden können. Ohne das Authorize -Attribut kann ein verbundener Client auf jede öffentliche Methode auf dem Hub zugreifen.

Wenn Sie eine Rolle mit dem Namen "Admin" in Ihrer Webanwendung definiert haben, können Sie angeben, dass nur Benutzer in dieser Rolle mit dem folgenden Code auf einen Hub zugreifen können.

[Authorize(Roles = "Admin")] 
public class AdminAuthHub : Hub 
{ 
}

Oder Sie können angeben, dass ein Hub eine Methode enthält, die für alle Benutzer verfügbar ist, und eine zweite Methode, die nur für authentifizierte Benutzer verfügbar ist, wie unten gezeigt.

public class SampleHub : Hub 
{ 
    public void UnrestrictedSend(string message){ . . . } 

    [Authorize] 
    public void AuthenticatedSend(string message){ . . . } 
}

In den folgenden Beispielen werden verschiedene Autorisierungsszenarien behandelt:

  • [Authorize] – nur authentifizierte Benutzer
  • [Authorize(Roles = "Admin,Manager")] – nur authentifizierte Benutzer in den angegebenen Rollen
  • [Authorize(Users = "user1,user2")] – nur authentifizierte Benutzer mit den angegebenen Benutzernamen
  • [Authorize(RequireOutgoing=false)] – Nur authentifizierte Benutzer können den Hub aufrufen, aber Anrufe vom Server zurück an Clients sind nicht durch Autorisierung eingeschränkt, z. B. wenn nur bestimmte Benutzer eine Nachricht senden können, aber alle anderen die Nachricht empfangen können. Die RequireOutgoing-Eigenschaft kann nur auf den gesamten Hub angewendet werden, nicht auf einzelne Methoden innerhalb des Hubs. Wenn RequireOutgoing nicht auf false festgelegt ist, werden vom Server nur Benutzer aufgerufen, die die Autorisierungsanforderung erfüllen.

Authentifizierung für alle Hubs erforderlich

Sie können die Authentifizierung für alle Hubs und Hubmethoden in Ihrer Anwendung anfordern, indem Sie beim Starten der Anwendung die RequireAuthentication-Methode aufrufen. Sie können diese Methode verwenden, wenn Sie über mehrere Hubs verfügen und eine Authentifizierungsanforderung für alle erzwingen möchten. Mit dieser Methode können Sie keine Anforderungen für die Rollen-, Benutzer- oder ausgehende Autorisierung angeben. Sie können nur angeben, dass der Zugriff auf die Hubmethoden auf authentifizierte Benutzer beschränkt ist. Sie können das Authorize-Attribut jedoch weiterhin auf Hubs oder Methoden anwenden, um zusätzliche Anforderungen anzugeben. Alle Anforderungen, die Sie in einem Attribut angeben, werden der Grundlegenden Anforderung der Authentifizierung hinzugefügt.

Das folgende Beispiel zeigt eine Startdatei, die alle Hubmethoden auf authentifizierte Benutzer beschränkt.

public partial class Startup {
    public void Configuration(IAppBuilder app) {
        app.MapSignalR();
        GlobalHost.HubPipeline.RequireAuthentication();
    }
}

Wenn Sie die RequireAuthentication() -Methode aufrufen, nachdem eine SignalR-Anforderung verarbeitet wurde, löst SignalR eine Ausnahme aus InvalidOperationException . SignalR löst diese Ausnahme aus, da Sie der HubPipeline kein Modul hinzufügen können, nachdem die Pipeline aufgerufen wurde. Das vorherige Beispiel zeigt das Aufrufen der RequireAuthentication -Methode in der Configuration Methode, die einmal vor der Behandlung der ersten Anforderung ausgeführt wird.

Angepasste Autorisierung

Wenn Sie anpassen müssen, wie die Autorisierung bestimmt wird, können Sie eine Klasse erstellen, die von AuthorizeAttribute der UserAuthorized-Methode abgeleitet und überschrieben wird. Für jede Anforderung ruft SignalR diese Methode auf, um zu bestimmen, ob der Benutzer zum Abschließen der Anforderung autorisiert ist. In der überschriebenen Methode stellen Sie die erforderliche Logik für Ihr Autorisierungsszenario bereit. Das folgende Beispiel zeigt, wie Die Autorisierung über anspruchsbasierte Identität erzwungen wird.

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class AuthorizeClaimsAttribute : AuthorizeAttribute
{
    protected override bool UserAuthorized(System.Security.Principal.IPrincipal user)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }

        var principal = user as ClaimsPrincipal;

        if (principal != null)
        {
            Claim authenticated = principal.FindFirst(ClaimTypes.Authentication);
            if (authenticated != null && authenticated.Value == "true")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
}

Übergeben von Authentifizierungsinformationen an Clients

Möglicherweise müssen Sie Authentifizierungsinformationen im Code verwenden, der auf dem Client ausgeführt wird. Sie übergeben die erforderlichen Informationen beim Aufrufen der Methoden auf dem Client. Beispielsweise kann eine Chatanwendungsmethode den Benutzernamen der Person, die eine Nachricht postet, als Parameter übergeben, wie unten gezeigt.

public Task SendChatMessage(string message)
{
    string name;
    var user = Context.User;

    if (user.Identity.IsAuthenticated)
    {
        name = user.Identity.Name;
    }
    else
    {
        name = "anonymous";
    }
    return Clients.All.addMessageToPage(name, message);
}

Alternativ können Sie ein -Objekt erstellen, um die Authentifizierungsinformationen darzustellen, und dieses Objekt wie unten dargestellt als Parameter übergeben.

public class SampleHub : Hub
{
    public override Task OnConnected()
    {
        return Clients.All.joined(GetAuthInfo());
    }

    protected object GetAuthInfo()
    {
        var user = Context.User;
        return new
        {
            IsAuthenticated = user.Identity.IsAuthenticated,
            IsAdmin = user.IsInRole("Admin"),
            UserName = user.Identity.Name
        };
    }
}

Sie sollten niemals die Verbindungs-ID eines Clients an andere Clients übergeben, da ein böswilliger Benutzer sie verwenden könnte, um eine Anforderung von diesem Client nachzuahmen.

Authentifizierungsoptionen für .NET-Clients

Wenn Sie über einen .NET-Client verfügen, z. B. eine Konsolen-App, die mit einem Hub interagiert, der auf authentifizierte Benutzer beschränkt ist, können Sie die Anmeldeinformationen für die Authentifizierung in einem Cookie, dem Verbindungsheader oder einem Zertifikat übergeben. Die Beispiele in diesem Abschnitt zeigen, wie Sie diese verschiedenen Methoden zur Authentifizierung eines Benutzers verwenden. Es handelt sich nicht um voll funktionsfähige SignalR-Apps. Weitere Informationen zu .NET-Clients mit SignalR finden Sie unter Hubs-API-Leitfaden – .NET-Client.

Wenn Ihr .NET-Client mit einem Hub interagiert, der ASP.NET Formularauthentifizierung verwendet, müssen Sie das Authentifizierungscooky für die Verbindung manuell festlegen. Sie fügen das Cookie der CookieContainer Eigenschaft des HubConnection-Objekts hinzu. Das folgende Beispiel zeigt eine Konsolen-App, die ein Authentifizierungscooky von einer Webseite abruft und dieses Cookie der Verbindung hinzufügt.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        Cookie returnedCookie;

        Console.Write("Enter user name: ");
        string username = Console.ReadLine();

        Console.Write("Enter password: ");
        string password = Console.ReadLine();

        var authResult = AuthenticateUser(username, password, out returnedCookie);

        if (authResult)
        {
            connection.CookieContainer = new CookieContainer();
            connection.CookieContainer.Add(returnedCookie);
            Console.WriteLine("Welcome " + username);
        }
        else
        {
            Console.WriteLine("Login failed");
        }    
    }

    private static bool AuthenticateUser(string user, string password, out Cookie authCookie)
    {
        var request = WebRequest.Create("https://www.contoso.com/RemoteLogin") as HttpWebRequest;
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.CookieContainer = new CookieContainer();

        var authCredentials = "UserName=" + user + "&Password=" + password;
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(authCredentials);
        request.ContentLength = bytes.Length;
        using (var requestStream = request.GetRequestStream())
        {
            requestStream.Write(bytes, 0, bytes.Length);
        }

        using (var response = request.GetResponse() as HttpWebResponse)
        {
            authCookie = response.Cookies[FormsAuthentication.FormsCookieName];
        }

        if (authCookie != null)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Die Konsolen-App stellt die Anmeldeinformationen an www.contoso.com/RemoteLogin die auf eine leere Seite verweisen können, die die folgende CodeBehind-Datei enthält.

using System;
using System.Web.Security;

namespace SignalRWithConsoleChat
{
    public partial class RemoteLogin : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string username = Request["UserName"];
            string password = Request["Password"];
            bool result = Membership.ValidateUser(username, password);
            if (result)
            {
                FormsAuthentication.SetAuthCookie(username, false);
            }
        }
    }
}

Windows-Authentifizierung

Wenn Sie Windows-Authentifizierung verwenden, können Sie die Anmeldeinformationen des aktuellen Benutzers mit der DefaultCredentials-Eigenschaft übergeben. Sie legen die Anmeldeinformationen für die Verbindung auf den Wert von DefaultCredentials fest.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        connection.Credentials = CredentialCache.DefaultCredentials;
        connection.Start().Wait();
    }
}

Verbindungsheader

Wenn Ihre Anwendung keine Cookies verwendet, können Sie Benutzerinformationen im Verbindungsheader übergeben. Beispielsweise können Sie ein Token im Verbindungsheader übergeben.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        connection.Headers.Add("myauthtoken", /* token data */);
        connection.Start().Wait();
    }
}

Anschließend überprüfen Sie im Hub das Token des Benutzers.

Zertifikat

Sie können ein Clientzertifikat übergeben, um den Benutzer zu überprüfen. Sie fügen das Zertifikat beim Erstellen der Verbindung hinzu. Das folgende Beispiel zeigt nur, wie sie der Verbindung ein Clientzertifikat hinzufügen. Die vollständige Konsolen-App wird nicht angezeigt. Es verwendet die X509Certificate-Klasse , die verschiedene Möglichkeiten zum Erstellen des Zertifikats bietet.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        connection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
        connection.Start().Wait();
    }
}