Authentifizierung und Autorisierung für SignalR-Hubs (SignalR 1.x)

von Patrick Fletcher, Tom FitzMacken

Warnung

Diese Dokumentation ist nicht für die neueste Version von SignalR vorgesehen. 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.

Überblick

Dieses Thema enthält folgende Abschnitte:

Autorisieren des Attributs

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. Die verschiedenen Arten von Autorisierungsanforderungen, die Sie anwenden können, werden unten gezeigt. Ohne das Authorize -Attribut sind alle öffentlichen Methoden auf dem Hub für einen Client verfügbar, der mit dem Hub verbunden ist.

Wenn Sie in Ihrer Webanwendung eine Rolle namens "Admin" 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 Aufrufe vom Server an Clients sind nicht durch autorisierungsbeschrä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 nur Benutzer vom Server aufgerufen, die die Autorisierungsanforderung erfüllen.

Authentifizierung für alle Hubs erforderlich

Sie können eine Authentifizierung für alle Hubs und Hubmethoden in Ihrer Anwendung anfordern, indem Sie die RequireAuthentication-Methode aufrufen, wenn die Anwendung gestartet wird. 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 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 Attributen angeben, werden zusätzlich zur grundlegenden Anforderung der Authentifizierung angewendet.

Das folgende Beispiel zeigt eine Global.asax-Datei, die alle Hubmethoden auf authentifizierte Benutzer beschränkt.

public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHubs();
        GlobalHost.HubPipeline.RequireAuthentication();
    }
}

Wenn Sie die RequireAuthentication() -Methode aufrufen, nachdem eine SignalR-Anforderung verarbeitet wurde, löst SignalR eine Ausnahme aus InvalidOperationException . Diese Ausnahme wird ausgelöst, weil Sie der HubPipeline kein Modul hinzufügen können, nachdem die Pipeline aufgerufen wurde. Das vorherige Beispiel zeigt den Aufruf der RequireAuthentication -Methode in der Application_Start -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 . Diese Methode wird für jede Anforderung aufgerufen, 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 = (ClaimsPrincipal)user;

        if (principal != null)
        {
            Claim authenticated = principal.FindFirst(ClaimTypes.Authentication);
            return authenticated.Value == "true" ? true : 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, wenn Sie die Methoden auf dem Client aufrufen. Beispielsweise könnte eine Chatanwendungsmethode den Benutzernamen der Person, die eine Nachricht veröffentlicht, 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 gezeigt 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, im Verbindungsheader oder in einem Zertifikat übergeben. Die Beispiele in diesem Abschnitt zeigen, wie Sie diese verschiedenen Methoden für die 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 Authentifizierungscookies von einer Webseite abruft und dieses Cookie der Verbindung hinzufügt. Die URL https://www.contoso.com/RemoteLogin im Beispiel verweist auf eine Webseite, die Sie erstellen müssen. Die Seite ruft den bereitgestellten Benutzernamen und das kennwort ab und versucht, den Benutzer mit den Anmeldeinformationen anzumelden.

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. Sie können beispielsweise 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 würden Sie im Hub das Token des Benutzers überprüfen.

Zertifikat

Sie können ein Clientzertifikat übergeben, um den Benutzer zu überprüfen. Sie fügen das Zertifikat beim Erstellen der Verbindung hinzu. Im folgenden Beispiel wird nur gezeigt, wie der Verbindung ein Clientzertifikat hinzugefügt wird. 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();
    }
}