Uwierzytelnianie i autoryzacja dla centrów usługi SignalR

Autor: Patrick Fletcher, Tom FitzMacken

Ostrzeżenie

Ta dokumentacja nie dotyczy 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.

Wersje oprogramowania używane w tym temacie

Poprzednie wersje tego tematu

Aby uzyskać informacje o wcześniejszych wersjach usługi SignalR, zobacz SignalR Starsze wersje.

Pytania i komentarze

Przekaż opinię na temat tego, jak ci się podobał ten samouczek i co możemy ulepszyć w komentarzach w dolnej części strony. Jeśli masz pytania, które nie są bezpośrednio związane z tym samouczkiem, możesz opublikować je na forum ASP.NET SignalR lub StackOverflow.com.

Omówienie

Ten temat zawiera następujące sekcje:

Autoryzowanie atrybutu

Usługa SignalR udostępnia atrybut Autoryzuj , 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 Authorize stosowany 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. Ten temat zawiera przykłady różnych typów wymagań dotyczących autoryzacji, które można zastosować. Bez atrybutu Authorize połączony klient może uzyskać dostęp do dowolnej metody publicznej w centrum.

Jeśli zdefiniowano rolę o nazwie "Administracja" w aplikacji internetowej, możesz 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żna też określić, że centrum zawiera jedną metodę dostępną dla wszystkich użytkowników, a drugą metodę dostępną 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ć komunikat. Właściwość RequireOutgoing może być stosowana tylko do całego centrum, a nie do metod poszczególnych w centrum. Jeśli ustawienie RequireOutgoing nie ma wartości false, tylko użytkownicy spełniający wymagania autoryzacji są wywoływani z serwera.

Wymagaj uwierzytelniania dla wszystkich centrów

Możesz wymagać uwierzytelniania dla wszystkich centrów i metod centrów w aplikacji, 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ć wymagań dotyczących roli, użytkownika lub 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 stosować atrybut Authorize do centrów lub metod w celu określenia dodatkowych wymagań. Wszelkie wymagania określone w atrybucie są dodawane do podstawowego wymagania uwierzytelniania.

W poniższym przykładzie pokazano plik startowy, który ogranicza wszystkie metody centrum do uwierzytelnionych użytkowników.

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

Jeśli wywołasz metodę po przetworzeniu RequireAuthentication() żądania usługi SignalR, usługa SignalR zgłosi InvalidOperationException wyjątek. Usługa SignalR zgłasza ten wyjątek, ponieważ nie można dodać modułu do interfejsu HubPipeline po wywołaniu potoku. W poprzednim przykładzie pokazano wywołanie RequireAuthentication metody w metodzie Configuration , 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ą. Dla każdego żądania usługa SignalR wywołuje tę metodę, aby określić, czy użytkownik ma autoryzację do ukończenia żądania. W metodzie zastępowanej należy podać logikę niezbędną 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 = 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;
        }
    }
}

Przekazywanie informacji o uwierzytelnianiu do klientów

Może być konieczne użycie informacji uwierzytelniania w kodzie uruchomionym 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 mógł go użyć 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 współdziała z centrum ograniczonym do uwierzytelnionych użytkowników, możesz przekazać poświadczenia uwierzytelniania w pliku cookie, nagłówku połączenia lub certyfikacie. W przykładach w tej sekcji pokazano, jak używać tych różnych metod uwierzytelniania użytkownika. Nie są one w pełni funkcjonalnymi aplikacjami SignalR. Aby uzyskać więcej informacji na temat klientów platformy .NET z usługą SignalR, zobacz Przewodnik po interfejsie API usługi Hubs — klient .NET.

Gdy klient platformy .NET korzysta z centrum korzystającego z uwierzytelniania ASP.NET Forms, należy ręcznie ustawić plik cookie uwierzytelniania na połączeniu. Plik cookie jest dodany do CookieContainer właściwości obiektu HubConnection . W poniższym przykładzie pokazano aplikację konsolową, która pobiera plik cookie uwierzytelniania ze strony internetowej i dodaje ten plik cookie do połączenia.

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 związany z 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 . Należy ustawić poświadczenia dla połączenia z wartością 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 używa plików cookie, możesz przekazać informacje o użytkowniku w nagłówku połączenia. Na przykład możesz 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 zweryfikujesz token użytkownika.

Certyfikat

Aby zweryfikować użytkownika, możesz przekazać certyfikat klienta. Certyfikat jest dodany podczas tworzenia połączenia. W poniższym przykładzie pokazano tylko sposób dodawania certyfikatu klienta do połączenia; nie wyświetla pełnej aplikacji konsolowej. Używa klasy X509Certificate , która zapewnia 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();
    }
}