Compartilhar via


Autenticação e autorização para hubs do SignalR (SignalR 1.x)

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.

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. Os diferentes tipos de requisitos de autorização que você pode aplicar são mostrados abaixo. Sem o Authorize atributo , todos os métodos públicos no hub estão disponíveis para um cliente conectado ao 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 a função, o usuário ou a autorização de 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 atributos é aplicado além do requisito básico de autenticação.

O exemplo a seguir mostra um arquivo Global.asax que restringe todos os métodos de hub a usuários autenticados.

public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHubs();
        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. Essa exceção é gerada porque você não pode adicionar um módulo ao HubPipeline depois que o pipeline foi invocado. O exemplo anterior mostra a chamada do RequireAuthentication método no Application_Start 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 . Esse método é chamado para cada solicitação 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 = (ClaimsPrincipal)user;

        if (principal != null)
        {
            Claim authenticated = principal.FindFirst(ClaimTypes.Authentication);
            return authenticated.Value == "true" ? true : 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. A URL https://www.contoso.com/RemoteLogin no exemplo aponta para uma página da Web que você precisaria criar. A página recuperaria o nome de usuário e a senha postados e tentaria fazer logon no usuário com as credenciais.

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