Uwierzytelnianie i autoryzacja centrów usługi SignalR (SignalR 1.x)

Autor: Patrick Fletcher, Tom FitzMacken

Ostrzeżenie

Ta dokumentacja nie jest przeznaczona dla najnowszej wersji usługi SignalR. Przyjrzyj się ASP.NET Core SignalR.

W tym temacie opisano sposób ograniczania, którzy użytkownicy lub role mogą uzyskiwać dostęp do metod centrum.

Omówienie

Ten temat zawiera następujące sekcje:

Autoryzowanie atrybutu

Usługa SignalR udostępnia atrybut Autoryzowanie , aby określić, którzy użytkownicy lub role mają dostęp do centrum lub metody. Ten atrybut znajduje się w Microsoft.AspNet.SignalR przestrzeni nazw. Atrybut jest stosowany Authorize do koncentratora lub określonych metod w centrum. Po zastosowaniu atrybutu Authorize do klasy centrum określone wymaganie autoryzacji jest stosowane do wszystkich metod w centrum. Poniżej przedstawiono różne typy wymagań dotyczących autoryzacji, które można zastosować. Bez atrybutu Authorize wszystkie publiczne metody w centrum są dostępne dla klienta połączonego z koncentratorem.

Jeśli zdefiniowano rolę o nazwie "Administracja" w aplikacji internetowej, można określić, że tylko użytkownicy w tej roli mogą uzyskiwać dostęp do centrum przy użyciu następującego kodu.

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

Możesz też określić, że centrum zawiera jedną metodę dostępną dla wszystkich użytkowników, a drugą metodę, która jest dostępna tylko dla uwierzytelnionych użytkowników, jak pokazano poniżej.

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

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

W poniższych przykładach przedstawiono różne scenariusze autoryzacji:

  • [Authorize] — tylko uwierzytelnieni użytkownicy
  • [Authorize(Roles = "Admin,Manager")] — tylko uwierzytelnieni użytkownicy w określonych rolach
  • [Authorize(Users = "user1,user2")] — tylko uwierzytelnieni użytkownicy z określonymi nazwami użytkowników
  • [Authorize(RequireOutgoing=false)] — tylko uwierzytelnieni użytkownicy mogą wywoływać centrum, ale wywołania z serwera z powrotem do klientów nie są ograniczone przez autoryzację, na przykład wtedy, gdy tylko niektórzy użytkownicy mogą wysyłać komunikat, ale wszyscy inni mogą odbierać wiadomość. Właściwość RequireOutgoing może być stosowana tylko do całego centrum, a nie dla metod indywidualnych w centrum. Jeśli pozycja RequireOutgoing nie jest ustawiona na wartość false, tylko użytkownicy spełniający wymagania dotyczące autoryzacji są wywoływani z serwera.

Wymagaj uwierzytelniania dla wszystkich centrów

Uwierzytelnianie dla wszystkich centrów i metod centrów w aplikacji można wymagać, wywołując metodę RequireAuthentication po uruchomieniu aplikacji. Możesz użyć tej metody, jeśli masz wiele centrów i chcesz wymusić wymaganie uwierzytelniania dla wszystkich z nich. W przypadku tej metody nie można określić roli, użytkownika ani autoryzacji wychodzącej. Można określić tylko, że dostęp do metod centrum jest ograniczony do uwierzytelnionych użytkowników. Można jednak nadal zastosować atrybut Autoryzuj do centrów lub metod w celu określenia dodatkowych wymagań. Wszelkie wymagania określone w atrybutach są stosowane oprócz podstawowego wymagania uwierzytelniania.

W poniższym przykładzie przedstawiono plik Global.asax, który ogranicza wszystkie metody koncentratora do uwierzytelnionych użytkowników.

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

Jeśli wywołasz metodę po przetworzeniu RequireAuthentication() żądania signalR, usługa SignalR zgłosi InvalidOperationException wyjątek. Ten wyjątek jest zgłaszany, ponieważ nie można dodać modułu do interfejsu HubPipeline po wywołaniu potoku. W poprzednim przykładzie pokazano wywołanie RequireAuthentication metody w Application_Start metodzie, która jest wykonywana jednorazowo przed obsługą pierwszego żądania.

Niestandardowa autoryzacja

Jeśli musisz dostosować sposób określania autoryzacji, możesz utworzyć klasę pochodzącą z AuthorizeAttribute metody UserAuthorized i zastąpić ją. Ta metoda jest wywoływana dla każdego żądania w celu określenia, czy użytkownik ma autoryzację do ukończenia żądania. W metodzie przesłonięcia należy podać niezbędną logikę dla scenariusza autoryzacji. W poniższym przykładzie pokazano, jak wymusić autoryzację za pomocą tożsamości opartej na oświadczeniach.

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

Przekazywanie informacji o uwierzytelnianiu do klientów

Może być konieczne użycie informacji uwierzytelniania w kodzie uruchamianym na kliencie. Wymagane informacje są przekazywane podczas wywoływania metod na kliencie. Na przykład metoda aplikacji czatu może przekazać jako parametr nazwę użytkownika osoby publikującej wiadomość, jak pokazano poniżej.

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

Możesz też utworzyć obiekt reprezentujący informacje uwierzytelniania i przekazać ten obiekt jako parametr, jak pokazano poniżej.

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

Nigdy nie należy przekazywać identyfikatora połączenia jednego klienta do innych klientów, ponieważ złośliwy użytkownik może użyć go do naśladowania żądania od tego klienta.

Opcje uwierzytelniania dla klientów platformy .NET

Jeśli masz klienta platformy .NET, takiego jak aplikacja konsolowa, która wchodzi w interakcję z koncentratorem ograniczonym do uwierzytelnionych użytkowników, możesz przekazać poświadczenia uwierzytelniania w pliku cookie, nagłówku połączenia lub certyfikatu. W przykładach w tej sekcji pokazano, jak używać tych różnych metod uwierzytelniania użytkownika. Nie są to w pełni funkcjonalne aplikacje SignalR. Aby uzyskać więcej informacji na temat klientów platformy .NET za pomocą usługi SignalR, zobacz Przewodnik po interfejsie API usługi Hubs — klient platformy .NET.

Gdy klient platformy .NET wchodzi w interakcję z centrum używającym uwierzytelniania ASP.NET Forms, musisz ręcznie ustawić plik cookie uwierzytelniania na połączeniu. Plik cookie jest dodany do CookieContainer właściwości obiektu HubConnection . Poniższy przykład przedstawia aplikację konsolową, która pobiera plik cookie uwierzytelniania ze strony internetowej i dodaje ten plik cookie do połączenia. Adres URL https://www.contoso.com/RemoteLogin w przykładzie wskazuje stronę internetową, którą należy utworzyć. Strona pobrałaby opublikowaną nazwę użytkownika i hasło oraz próbowała zalogować się za pomocą poświadczeń.

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

Aplikacja konsolowa publikuje poświadczenia do www.contoso.com/RemoteLogin , które mogą odwoływać się do pustej strony zawierającej następujący plik za kodem.

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

Uwierzytelnianie Windows

W przypadku korzystania z uwierzytelniania systemu Windows można przekazać poświadczenia bieżącego użytkownika przy użyciu właściwości DefaultCredentials . Poświadczenia dla połączenia należy ustawić na wartość DefaultCredentials.

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

Nagłówek połączenia

Jeśli aplikacja nie korzysta z plików cookie, możesz przekazać informacje o użytkowniku w nagłówku połączenia. Można na przykład przekazać token w nagłówku połączenia.

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

Następnie w centrum sprawdzisz token użytkownika.

Certyfikat

Możesz przekazać certyfikat klienta, aby zweryfikować użytkownika. Certyfikat należy dodać podczas tworzenia połączenia. W poniższym przykładzie pokazano tylko sposób dodawania certyfikatu klienta do połączenia; nie jest wyświetlana pełna aplikacja konsolowa. Używa klasy X509Certificate , która udostępnia kilka różnych sposobów tworzenia certyfikatu.

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