Partager via


Authentification et autorisation pour SignalR Hubs (SignalR 1.x)

par Patrick Fletcher, Tom FitzMacken

Avertissement

Cette documentation ne concerne pas la dernière version de SignalR. Consultez ASP.NET Core SignalR.

Cette rubrique explique comment restreindre les utilisateurs ou rôles qui peuvent accéder aux méthodes hub.

Vue d’ensemble

Cette rubrique contient les sections suivantes :

Autoriser l’attribut

SignalR fournit l’attribut Authorize pour spécifier quels utilisateurs ou rôles ont accès à un hub ou à une méthode. Cet attribut se trouve dans l’espace de Microsoft.AspNet.SignalR noms. Vous appliquez l’attribut Authorize à un hub ou à des méthodes particulières dans un hub. Lorsque vous appliquez l’attribut Authorize à une classe hub, l’exigence d’autorisation spécifiée est appliquée à toutes les méthodes du hub. Les différents types d’exigences d’autorisation que vous pouvez appliquer sont indiqués ci-dessous. Sans l’attribut Authorize , toutes les méthodes publiques sur le hub sont disponibles pour un client connecté au hub.

Si vous avez défini un rôle nommé « Administration » dans votre application web, vous pouvez spécifier que seuls les utilisateurs de ce rôle peuvent accéder à un hub avec le code suivant.

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

Vous pouvez également spécifier qu’un hub contient une méthode disponible pour tous les utilisateurs et une deuxième méthode uniquement disponible pour les utilisateurs authentifiés, comme indiqué ci-dessous.

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

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

Les exemples suivants traitent de différents scénarios d’autorisation :

  • [Authorize] : utilisateurs authentifiés uniquement
  • [Authorize(Roles = "Admin,Manager")] : uniquement les utilisateurs authentifiés dans les rôles spécifiés
  • [Authorize(Users = "user1,user2")] : uniquement les utilisateurs authentifiés avec les noms d’utilisateur spécifiés
  • [Authorize(RequireOutgoing=false)] : seuls les utilisateurs authentifiés peuvent appeler le hub, mais les appels du serveur aux clients ne sont pas limités par l’autorisation, par exemple, lorsque seuls certains utilisateurs peuvent envoyer un message, mais que tous les autres peuvent recevoir le message. La propriété RequireOutgoing ne peut être appliquée qu’à l’ensemble du hub, et non à des méthodes individuelles au sein du hub. Lorsque RequireOutgoing n’est pas défini sur false, seuls les utilisateurs qui répondent à l’exigence d’autorisation sont appelés à partir du serveur.

Exiger l’authentification pour tous les hubs

Vous pouvez exiger l’authentification pour tous les hubs et méthodes hub de votre application en appelant la méthode RequireAuthentication au démarrage de l’application. Vous pouvez utiliser cette méthode lorsque vous avez plusieurs hubs et que vous souhaitez appliquer une exigence d’authentification pour tous ces hubs. Avec cette méthode, vous ne pouvez pas spécifier de rôle, d’utilisateur ou d’autorisation sortante. Vous pouvez uniquement spécifier que l’accès aux méthodes hub est limité aux utilisateurs authentifiés. Toutefois, vous pouvez toujours appliquer l’attribut Authorize à des hubs ou des méthodes pour spécifier des exigences supplémentaires. Toute exigence que vous spécifiez dans les attributs est appliquée en plus de l’exigence de base de l’authentification.

L’exemple suivant montre un fichier Global.asax qui limite toutes les méthodes hub aux utilisateurs authentifiés.

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

Si vous appelez la méthode après le RequireAuthentication() traitement d’une requête SignalR, SignalR lève une InvalidOperationException exception. Cette exception est levée, car vous ne pouvez pas ajouter un module à hubPipeline une fois que le pipeline a été appelé. L’exemple précédent montre l’appel de la RequireAuthentication méthode dans la Application_Start méthode qui est exécutée une fois avant de gérer la première requête.

Autorisation personnalisée

Si vous devez personnaliser la façon dont l’autorisation est déterminée, vous pouvez créer une classe qui dérive de AuthorizeAttribute la méthode UserAuthorized et la remplace. Cette méthode est appelée pour chaque requête afin de déterminer si l’utilisateur est autorisé à effectuer la demande. Dans la méthode remplacée, vous fournissez la logique nécessaire pour votre scénario d’autorisation. L’exemple suivant montre comment appliquer l’autorisation via une identité basée sur les revendications.

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

Transmettre des informations d’authentification aux clients

Vous devrez peut-être utiliser les informations d’authentification dans le code qui s’exécute sur le client. Vous transmettez les informations requises lors de l’appel des méthodes sur le client. Par exemple, une méthode d’application de conversation peut passer en tant que paramètre le nom d’utilisateur de la personne qui publie un message, comme indiqué ci-dessous.

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

Vous pouvez également créer un objet pour représenter les informations d’authentification et passer cet objet en tant que paramètre, comme indiqué ci-dessous.

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

Vous ne devez jamais transmettre l’ID de connexion d’un client à d’autres clients, car un utilisateur malveillant peut l’utiliser pour imiter une demande de ce client.

Options d’authentification pour les clients .NET

Lorsque vous disposez d’un client .NET, tel qu’une application console, qui interagit avec un hub limité aux utilisateurs authentifiés, vous pouvez transmettre les informations d’identification d’authentification dans un cookie, l’en-tête de connexion ou un certificat. Les exemples de cette section montrent comment utiliser ces différentes méthodes pour authentifier un utilisateur. Il ne s’agit pas d’applications SignalR entièrement fonctionnelles. Pour plus d’informations sur les clients .NET avec SignalR, consultez Guide de l’API Hubs - Client .NET.

Lorsque votre client .NET interagit avec un hub qui utilise ASP.NET authentification par formulaire, vous devez définir manuellement le cookie d’authentification sur la connexion. Vous ajoutez le cookie à la CookieContainer propriété sur l’objet HubConnection . L’exemple suivant montre une application console qui récupère un cookie d’authentification à partir d’une page web et ajoute ce cookie à la connexion. L’URL https://www.contoso.com/RemoteLogin de l’exemple pointe vers une page web que vous devez créer. La page récupère le nom d’utilisateur et le mot de passe publiés et tente de se connecter à l’utilisateur avec les informations d’identification.

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

L’application console publie les informations d’identification dans www.contoso.com/RemoteLogin qui peut faire référence à une page vide contenant le fichier code-behind suivant.

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

Authentification Windows

Lorsque vous utilisez Authentification Windows, vous pouvez transmettre les informations d’identification de l’utilisateur actuel à l’aide de la propriété DefaultCredentials. Vous définissez les informations d’identification de la connexion sur la valeur de DefaultCredentials.

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

En-tête de connexion

Si votre application n’utilise pas de cookies, vous pouvez transmettre les informations utilisateur dans l’en-tête de connexion. Par exemple, vous pouvez passer un jeton dans l’en-tête de connexion.

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

Ensuite, dans le hub, vous vérifiez le jeton de l’utilisateur.

Certificat

Vous pouvez passer un certificat client pour vérifier l’utilisateur. Vous ajoutez le certificat lors de la création de la connexion. L’exemple suivant montre uniquement comment ajouter un certificat client à la connexion ; il n’affiche pas l’application console complète. Il utilise la classe X509Certificate qui fournit plusieurs façons différentes de créer le certificat.

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