Share via


Autenticazione e autorizzazione per SignalR Hubs (SignalR 1.x)

di Patrick Fletcher, Tom FitzMacken

Avviso

Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.

Questo argomento descrive come limitare quali utenti o ruoli possono accedere ai metodi dell'hub.

Panoramica

In questo argomento sono incluse le sezioni seguenti:

Autorizzare l'attributo

SignalR fornisce l'attributo Autorizza per specificare quali utenti o ruoli hanno accesso a un hub o a un metodo. Questo attributo si trova nello Microsoft.AspNet.SignalR spazio dei nomi. Si applica l'attributo Authorize a un hub o a metodi specifici in un hub. Quando si applica l'attributo a una classe hub, il requisito di autorizzazione specificato viene applicato a tutti i metodi nell'hub Authorize . Di seguito sono riportati i diversi tipi di requisiti di autorizzazione che è possibile applicare. Senza l'attributo, tutti i metodi pubblici nell'hub Authorize sono disponibili per un client connesso all'hub.

Se è stato definito un ruolo denominato "Amministrazione" nell'applicazione Web, è possibile specificare che solo gli utenti in tale ruolo possono accedere a un hub con il codice seguente.

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

In alternativa, è possibile specificare che un hub contiene un metodo disponibile per tutti gli utenti e un secondo metodo disponibile solo per gli utenti autenticati, come illustrato di seguito.

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

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

Negli esempi seguenti vengono illustrati diversi scenari di autorizzazione:

  • [Authorize] – solo utenti autenticati
  • [Authorize(Roles = "Admin,Manager")] : solo gli utenti autenticati nei ruoli specificati
  • [Authorize(Users = "user1,user2")] : solo gli utenti autenticati con i nomi utente specificati
  • [Authorize(RequireOutgoing=false)] : solo gli utenti autenticati possono richiamare l'hub, ma le chiamate dal server di nuovo ai client non sono limitate dall'autorizzazione, ad esempio, quando solo alcuni utenti possono inviare un messaggio, ma tutti gli altri possono ricevere il messaggio. La proprietà RequireOutgoing può essere applicata solo all'intero hub, non sui singoli metodi all'interno dell'hub. Quando RequireOutgoing non è impostato su false, vengono chiamati solo gli utenti che soddisfano il requisito di autorizzazione dal server.

Richiedere l'autenticazione per tutti gli hub

È possibile richiedere l'autenticazione per tutti i metodi hub e hub nell'applicazione chiamando il metodo RequireAuthentication all'avvio dell'applicazione. È possibile usare questo metodo quando sono presenti più hub e si vuole applicare un requisito di autenticazione per tutti. Con questo metodo non è possibile specificare il ruolo, l'utente o l'autorizzazione in uscita. È possibile specificare solo che l'accesso ai metodi hub è limitato agli utenti autenticati. Tuttavia, è comunque possibile applicare l'attributo Authorize a hub o metodi per specificare requisiti aggiuntivi. Qualsiasi requisito specificato negli attributi viene applicato oltre al requisito di base dell'autenticazione.

Nell'esempio seguente viene illustrato un file Global.asax che limita tutti i metodi hub agli utenti autenticati.

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

Se si chiama il metodo dopo l'elaborazione RequireAuthentication() di una richiesta SignalR, SignalR genererà un'eccezione InvalidOperationException . Questa eccezione viene generata perché non è possibile aggiungere un modulo all'hubPipeline dopo che la pipeline è stata richiamata. L'esempio precedente mostra la chiamata al RequireAuthenticationApplication_Start metodo nel metodo che viene eseguito una sola volta prima di gestire la prima richiesta.

Autorizzazione personalizzata

Se è necessario personalizzare la modalità di determinazione dell'autorizzazione, è possibile creare una classe che deriva da AuthorizeAttribute e eseguire l'override del metodo UserAuthorized . Questo metodo viene chiamato per ogni richiesta per determinare se l'utente è autorizzato a completare la richiesta. Nel metodo sottoposto a override si specifica la logica necessaria per lo scenario di autorizzazione. Nell'esempio seguente viene illustrato come applicare l'autorizzazione tramite identità basata su attestazioni.

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

Passare le informazioni di autenticazione ai client

Potrebbe essere necessario usare le informazioni di autenticazione nel codice in esecuzione nel client. Le informazioni necessarie vengono passate quando si chiamano i metodi nel client. Ad esempio, un metodo applicazione chat potrebbe passare come parametro il nome utente della persona che pubblica un messaggio, come illustrato di seguito.

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

In alternativa, è possibile creare un oggetto per rappresentare le informazioni di autenticazione e passare tale oggetto come parametro, come illustrato di seguito.

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

Non è mai necessario passare l'ID connessione di un client ad altri client, in quanto un utente malintenzionato potrebbe usarlo per simulare una richiesta da tale client.

Opzioni di autenticazione per i client .NET

Quando si dispone di un client .NET, ad esempio un'app console, che interagisce con un hub limitato agli utenti autenticati, è possibile passare le credenziali di autenticazione in un cookie, l'intestazione di connessione o un certificato. Gli esempi di questa sezione illustrano come usare questi diversi metodi per l'autenticazione di un utente. Non sono app SignalR completamente funzionali. Per altre informazioni sui client .NET con SignalR, vedere Guida all'API hub - Client .NET.

Quando il client .NET interagisce con un hub che usa ASP.NET Autenticazione moduli, sarà necessario impostare manualmente il cookie di autenticazione sulla connessione. Aggiungere il cookie alla CookieContainer proprietà nell'oggetto HubConnection . Nell'esempio seguente viene illustrata un'app console che recupera un cookie di autenticazione da una pagina Web e aggiunge tale cookie alla connessione. L'URL https://www.contoso.com/RemoteLogin nell'esempio punta a una pagina Web da creare. La pagina recupera il nome utente e la password pubblicati e tenta di accedere all'utente con le credenziali.

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'app console pubblica le credenziali per www.contoso.com/RemoteLogin che può fare riferimento a una pagina vuota contenente il file code-behind seguente.

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

Autenticazione di Windows

Quando si usa autenticazione di Windows, è possibile passare le credenziali dell'utente corrente usando la proprietà DefaultCredentials. Si impostano le credenziali per la connessione al valore di DefaultCredentials.

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

Intestazione connessione

Se l'applicazione non usa cookie, è possibile passare le informazioni utente nell'intestazione di connessione. Ad esempio, è possibile passare un token nell'intestazione di connessione.

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

Quindi, nell'hub verificare il token dell'utente.

Certificato

È possibile passare un certificato client per verificare l'utente. Si aggiunge il certificato durante la creazione della connessione. Nell'esempio seguente viene illustrato solo come aggiungere un certificato client alla connessione; non mostra l'app console completa. Usa la classe X509Certificate che offre diversi modi per creare il certificato.

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