Compartilhar via


Autenticação e autorização para hubs do SignalR

por Patrick Fletcher, Tom FitzMacken

Aviso

Esta documentação não é para a versão mais recente do SignalR. Dê uma olhada em ASP.NET Core SignalR.

Este tópico descreve como restringir quais usuários ou funções podem acessar métodos de hub.

Versões de software usadas neste tópico

Versões anteriores deste tópico

Para obter informações sobre versões anteriores do SignalR, consulte Versões mais antigas do SignalR.

Perguntas e comentários

Deixe comentários sobre como você gostou deste tutorial e o que poderíamos melhorar nos comentários na parte inferior da página. Se você tiver dúvidas que não estão diretamente relacionadas ao tutorial, poderá postá-las no fórum do ASP.NET SignalR ou StackOverflow.com.

Visão geral

Este tópico contém as seguintes seções:

Autorizar atributo

O SignalR fornece o atributo Authorize para especificar quais usuários ou funções têm acesso a um hub ou método. Esse atributo está localizado no Microsoft.AspNet.SignalR namespace . Você aplica o Authorize atributo a um hub ou a métodos específicos em um hub. Quando você aplica o Authorize atributo a uma classe de hub, o requisito de autorização especificado é aplicado a todos os métodos no hub. Este tópico fornece exemplos dos diferentes tipos de requisitos de autorização que você pode aplicar. Sem o Authorize atributo , um cliente conectado pode acessar qualquer método público no hub.

Se você tiver definido uma função chamada "Administração" em seu aplicativo Web, poderá especificar que somente os usuários nessa função podem acessar um hub com o código a seguir.

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

Ou você pode especificar que um hub contém um método que está disponível para todos os usuários e um segundo método que só está disponível para usuários autenticados, conforme mostrado abaixo.

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

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

Os exemplos a seguir abordam diferentes cenários de autorização:

  • [Authorize] – somente usuários autenticados
  • [Authorize(Roles = "Admin,Manager")] – somente usuários autenticados nas funções especificadas
  • [Authorize(Users = "user1,user2")] – somente usuários autenticados com os nomes de usuário especificados
  • [Authorize(RequireOutgoing=false)] – somente usuários autenticados podem invocar o hub, mas as chamadas do servidor de volta para clientes não são limitadas por autorização, como, quando apenas determinados usuários podem enviar uma mensagem, mas todos os outros podem receber a mensagem. A propriedade RequireOutgoing só pode ser aplicada a todo o hub, não aos métodos individuais dentro do hub. Quando RequireOutgoing não está definido como false, somente os usuários que atendem ao requisito de autorização são chamados do servidor.

Exigir autenticação para todos os hubs

Você pode exigir autenticação para todos os hubs e métodos de hub em seu aplicativo chamando o método RequireAuthentication quando o aplicativo é iniciado. Você pode usar esse método quando tiver vários hubs e quiser impor um requisito de autenticação para todos eles. Com esse método, você não pode especificar requisitos para autorização de função, usuário ou saída. Você só pode especificar que o acesso aos métodos de hub é restrito a usuários autenticados. No entanto, você ainda pode aplicar o atributo Authorize a hubs ou métodos para especificar requisitos adicionais. Qualquer requisito especificado em um atributo é adicionado ao requisito básico de autenticação.

O exemplo a seguir mostra um arquivo de inicialização que restringe todos os métodos de hub para usuários autenticados.

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

Se você chamar o RequireAuthentication() método depois que uma solicitação do SignalR for processada, o SignalR lançará uma InvalidOperationException exceção. O SignalR gera essa exceção porque você não pode adicionar um módulo ao HubPipeline depois que o pipeline é invocado. O exemplo anterior mostra a chamada do RequireAuthentication método no Configuration método que é executado uma vez antes de lidar com a primeira solicitação.

Autorização personalizada

Se você precisar personalizar como a autorização é determinada, poderá criar uma classe que deriva de AuthorizeAttribute e substituir o método UserAuthorized . Para cada solicitação, o SignalR invoca esse método para determinar se o usuário está autorizado a concluir a solicitação. No método substituído, você fornece a lógica necessária para seu cenário de autorização. O exemplo a seguir mostra como impor a autorização por meio da identidade baseada em declarações.

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

Passar informações de autenticação para clientes

Talvez seja necessário usar informações de autenticação no código que é executado no cliente. Você passa as informações necessárias ao chamar os métodos no cliente. Por exemplo, um método de aplicativo de chat pode passar como um parâmetro o nome de usuário da pessoa que está postando uma mensagem, conforme mostrado abaixo.

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

Ou você pode criar um objeto para representar as informações de autenticação e passar esse objeto como um parâmetro, conforme mostrado abaixo.

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

Você nunca deve passar a ID de conexão de um cliente para outros clientes, pois um usuário mal-intencionado poderia usá-la para imitar uma solicitação desse cliente.

Opções de autenticação para clientes .NET

Quando você tem um cliente .NET, como um aplicativo de console, que interage com um hub limitado a usuários autenticados, você pode passar as credenciais de autenticação em um cookie, o cabeçalho de conexão ou um certificado. Os exemplos nesta seção mostram como usar esses métodos diferentes para autenticar um usuário. Eles não são aplicativos do SignalR totalmente funcionais. Para obter mais informações sobre clientes .NET com o SignalR, consulte Guia da API de Hubs – Cliente .NET.

Quando o cliente .NET interage com um hub que usa ASP.NET Autenticação de Formulários, você precisará definir manualmente o cookie de autenticação na conexão. Você adiciona o cookie à CookieContainer propriedade no objeto HubConnection . O exemplo a seguir mostra um aplicativo de console que recupera um cookie de autenticação de uma página da Web e adiciona esse cookie à conexão.

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

O aplicativo de console posta as credenciais em www.contoso.com/RemoteLogin que podem se referir a uma página vazia que contém o arquivo code-behind a seguir.

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

Autenticação do Windows

Ao usar autenticação do Windows, você pode passar as credenciais do usuário atual usando a propriedade DefaultCredentials. Defina as credenciais para a conexão com o valor de DefaultCredentials.

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

Cabeçalho de conexão

Se o aplicativo não estiver usando cookies, você poderá passar informações do usuário no cabeçalho da conexão. Por exemplo, você pode passar um token no cabeçalho de conexão.

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

Em seguida, no hub, você verificaria o token do usuário.

Certificado

Você pode passar um certificado de cliente para verificar o usuário. Você adiciona o certificado ao criar a conexão. O exemplo a seguir mostra apenas como adicionar um certificado de cliente à conexão; ele não mostra o aplicativo de console completo. Ele usa a classe X509Certificate , que fornece várias maneiras diferentes de criar o certificado.

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