Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Pierwsze żądanie między klientem a serwerem to żądanie negocjacji. W przypadku korzystania z własnego serwera SignalR należy użyć żądania w celu nawiązania połączenia między klientem a serwerem. W przypadku korzystania z usługi Azure SignalR Service klienci łączą się z usługą zamiast z serwerem aplikacji. W tym artykule omówiono pojęcia dotyczące protokołów negocjacji i sposobów dostosowywania punktu końcowego negocjacji.
Ważne
Nieprzetworzone parametry połączenia są wyświetlane tylko w tym artykule w celach demonstracyjnych.
Parametry połączenia zawiera informacje o autoryzacji wymagane do uzyskania dostępu do usługi Azure SignalR Service przez aplikację. Klucz dostępu wewnątrz parametry połączenia jest podobny do hasła głównego usługi. W środowiskach produkcyjnych zawsze chroń klucze dostępu. Usługa Azure Key Vault umożliwia bezpieczne zarządzanie kluczami i obracanie ich oraz zabezpieczanie parametry połączenia przy użyciu identyfikatora Entra firmy Microsoft i autoryzowania dostępu za pomocą identyfikatora Entra firmy Microsoft.
Unikaj dystrybuowania kluczy dostępu do innych użytkowników, kodowania ich lub zapisywania ich w dowolnym miejscu w postaci zwykłego tekstu, który jest dostępny dla innych użytkowników. Obracanie kluczy, jeśli uważasz, że mogły one zostać naruszone.
Co to są negocjacje?
Odpowiedź na POST [endpoint-base]/negotiate
żądanie zawiera jeden z trzech typów odpowiedzi:
Odpowiedź zawierająca element
connectionId
, który identyfikuje połączenie na serwerze i listę transportów, które obsługuje serwer:{ "connectionToken":"05265228-1e2c-46c5-82a1-6a5bcc3f0143", "connectionId":"807809a5-31bf-470d-9e23-afaee35d8a0d", "negotiateVersion":1, "availableTransports":[ { "transport": "WebSockets", "transferFormats": [ "Text", "Binary" ] }, { "transport": "ServerSentEvents", "transferFormats": [ "Text" ] }, { "transport": "LongPolling", "transferFormats": [ "Text", "Binary" ] } ] }
Ładunek zwracany przez ten punkt końcowy zawiera następujące dane:
- Wartość jest wymagana
connectionId
przezLongPolling
transport iServerSentEvents
w celu skorelowania wysyłania i odbierania. - Wartość
negotiateVersion
to wersja protokołu negocjacji używana między serwerem a klientem, zobacz Protokoły transportu.-
negotiateVersion: 0
ZwracaconnectionId
tylko wartość , a klient powinien używać wartościconnectionId
jakid
w żądaniach połączenia. -
negotiateVersion: 1
funkcja zwracaconnectionId
wartości i ,connectionToken
a klient powinien używać wartościconnectionToken
jakid
w żądaniach łączenia.
-
- Na
availableTransports
liście opisano transporty obsługiwane przez serwer. Dla każdego transportu ładunek zawiera nazwę transportu (transport
) oraz listę formatów transferu, które obsługuje transport (transferFormats
).
- Wartość jest wymagana
Odpowiedź przekierowania, która informuje klienta, który adres URL i (opcjonalnie) token dostępu do użycia w wyniku:
{ "url": "https://<Server endpoint>/<Hub name>", "accessToken": "<accessToken>" }
Ładunek zwracany przez ten punkt końcowy zawiera następujące dane:
- Wartość
url
to adres URL, z którym klient powinien nawiązać połączenie. - Wartość
accessToken
jest opcjonalnym tokenem elementu nośnego na potrzeby uzyskiwania dostępu do określonego adresu URL.
- Wartość
Odpowiedź zawierająca
error
wpis, który powinien zatrzymać próbę połączenia:{ "error": "This connection is not allowed." }
Ładunek zwracany przez ten punkt końcowy zawiera następujące dane:
- Ciąg
error
zawiera szczegółowe informacje o tym, dlaczego negocjowanie nie powiodło się.
- Ciąg
W przypadku korzystania z usługi Azure SignalR Service klienci łączą się z usługą zamiast z serwerem aplikacji. Istnieją trzy kroki nawiązywania trwałych połączeń między klientem a usługą Azure SignalR Service:
Klient wysyła żądanie negocjacji do serwera aplikacji.
Serwer aplikacji używa zestawu SDK usługi Azure SignalR Service do zwrócenia odpowiedzi przekierowania zawierającej adres URL usługi Azure SignalR Service i token dostępu.
W przypadku ASP.NET Core SignalR typowa odpowiedź przekierowania wygląda następująco:
{ "url":"https://<SignalR name>.service.signalr.net/client/?hub=<Hub name>&...", "accessToken":"<accessToken>" }
Po odebraniu odpowiedzi przekierowania klient używa adresu URL i tokenu dostępu do nawiązywania połączenia z usługą SignalR Service. Następnie usługa kieruje klienta do serwera aplikacji.
Ważne
W przypadku własnego signalR niektórzy użytkownicy mogą pominąć negocjacje klienta, gdy klienci obsługują tylko protokół WebSocket i zapisują rundę negocjacji. Jednak podczas pracy z usługą Azure SignalR Service klienci powinni zawsze poprosić zaufany serwer lub zaufane centrum uwierzytelniania o utworzenie tokenu dostępu. Nie należy więc ustawiać SkipNegotiation
true
wartości na wartość po stronie klienta.
SkipNegotiation
oznacza, że klienci muszą samodzielnie skompilować token dostępu. To ustawienie wiąże się z ryzykiem bezpieczeństwa, że klient może wykonać dowolne czynności w punkcie końcowym usługi.
Co można zrobić podczas negocjacji?
Ustawienia niestandardowe dla połączeń klienckich
Możesz uruchomić bramę połączenia klienta, aby dostosować ustawienia pod kątem zabezpieczeń lub potrzeb biznesowych. Na przykład:
- Użyj krótkiej
AccessTokenLifetime
wartości dla zabezpieczeń. - Przekazywanie tylko niezbędnych informacji z oświadczeń klienta.
- Dodaj oświadczenia niestandardowe dla potrzeb biznesowych.
services.AddSignalR().AddAzureSignalR(options =>
{
// Pass only necessary information in the negotiation step
options.ClaimsProvider = context => new[]
{
new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"]),
new Claim("<Custom Claim Name>", "<Custom Claim Value>")
};
options.AccessTokenLifetime = TimeSpan.FromMinutes(5);
});
Stickiness serwera
Jeśli masz wiele serwerów aplikacji, nie ma gwarancji (domyślnie), że serwer, który negocjuje, i serwer, który pobiera wywołanie centrum, są takie same. W niektórych przypadkach na serwerze aplikacji mogą być przechowywane lokalnie informacje o stanie klienta.
Na przykład w przypadku korzystania z platformy Blazor po stronie serwera stan interfejsu użytkownika jest zachowywany po stronie serwera. Dlatego chcesz, aby wszystkie żądania klientów przechodziły do tego samego serwera, w tym do połączenia usługi SignalR. Następnie należy włączyć tryb sticky serwera podczas Required
negocjacji:
services.AddSignalR().AddAzureSignalR(options => {
options.ServerStickyMode = ServerStickyMode.Required;
});
Routing niestandardowy w wielu punktach końcowych
Innym sposobem dostosowywania negocjacji jest wiele punktów końcowych. Ponieważ serwer aplikacji udostępnia adres URL usługi jako odpowiedź na negocjacje, serwer aplikacji może określić punkt końcowy, który ma powrócić do klientów na potrzeby równoważenia obciążenia i wydajności komunikacji. Oznacza to, że klient może nawiązać połączenie z najbliższym punktem końcowym usługi, aby zmniejszyć koszty ruchu.
// Sample of a custom router
private class CustomRouter : EndpointRouterDecorator
{
public override ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
{
// Override the negotiation behavior to get the endpoint from the query string
var endpointName = context.Request.Query["endpoint"];
if (endpointName.Count == 0)
{
context.Response.StatusCode = 400;
var response = Encoding.UTF8.GetBytes("Invalid request");
context.Response.Body.Write(response, 0, response.Length);
return null;
}
return endpoints.FirstOrDefault(s => s.Name == endpointName && s.Online) // Get the endpoint with the name that matches the incoming request
?? base.GetNegotiateEndpoint(context, endpoints); // Fall back to the default behavior to randomly select one from primary endpoints, or fall back to secondary when no primary ones are online
}
}
Zarejestruj również router w celu wstrzyknięcia zależności.
Nieprzetworzone parametry połączenia są wyświetlane tylko w tym artykule w celach demonstracyjnych. W środowiskach produkcyjnych zawsze chroń klucze dostępu. Usługa Azure Key Vault umożliwia bezpieczne zarządzanie kluczami i obracanie ich oraz zabezpieczanie parametry połączenia przy użyciu identyfikatora Entra firmy Microsoft i autoryzowania dostępu za pomocą identyfikatora Entra firmy Microsoft.
// Sample of configuring multiple endpoints and dependency injection
services.AddSingleton(typeof(IEndpointRouter), typeof(CustomRouter));
services.AddSignalR().AddAzureSignalR(
options =>
{
options.Endpoints = new ServiceEndpoint[]
{
new ServiceEndpoint(name: "east", connectionString: "<connectionString1>"),
new ServiceEndpoint(name: "west", connectionString: "<connectionString2>"),
new ServiceEndpoint("<connectionString3>")
};
});
Jak dodać punkt końcowy negocjacji klienta w trybie bezserwerowym?
W trybie bezserwerowym (Serverless
) żaden serwer nie akceptuje klientów usługi SignalR. Aby ułatwić ochronę parametry połączenia, należy przekierować klientów usługi SignalR z punktu końcowego negocjacji do usługi Azure SignalR Service zamiast przydzielać parametry połączenia wszystkim klientom usługi SignalR.
Najlepszym rozwiązaniem jest hostowanie punktu końcowego negocjacji. Następnie możesz użyć klientów usługi SignalR do tego punktu końcowego i pobrać adres URL usługi i token dostępu.
Zestaw SDK zarządzania usługą Azure SignalR Service
Negocjacje można uzyskać, pracując z zestawem SDK zarządzania.
Możesz użyć wystąpienia , aby wygenerować adres URL punktu końcowego ServiceHubContext
i odpowiedni token dostępu dla klientów usługi SignalR w celu nawiązania połączenia z usługą Azure SignalR Service:
var negotiationResponse = await serviceHubContext.NegotiateAsync(new (){ UserId = "<Your User Id>" });
Załóżmy, że punkt końcowy centrum to http://<Your Host Name>/<Your Hub Name>
. Następnie punkt końcowy negocjacji to http://<Your Host Name>/<Your Hub Name>/negotiate
. Po hostowanie punktu końcowego negocjacji można nawiązać połączenie z koncentratorem za pomocą klientów signalR:
var connection = new HubConnectionBuilder().WithUrl("http://<Your Host Name>/<Your Hub Name>").Build();
await connection.StartAsync();
Pełny przykład dotyczący sposobu używania zestawu SDK zarządzania do przekierowywania klientów signalR do usługi Azure SignalR Service w usłudze GitHub.
Rozszerzenie funkcji usługi Azure SignalR Service
Gdy używasz aplikacji funkcji platformy Azure, możesz pracować z rozszerzeniem funkcji. Oto przykład użycia w SignalRConnectionInfo
modelu izolowanego procesu roboczego w języku C#, który ułatwia tworzenie odpowiedzi na negocjacje:
[Function(nameof(Negotiate))]
public static string Negotiate([HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequestData req,
[SignalRConnectionInfoInput(HubName = "serverless")] string connectionInfo)
{
// The serialization of the connection info object is done by the framework. It should be camel case. The SignalR client respects the camel case response only.
return connectionInfo;
}
Ostrzeżenie
Dla uproszczenia pomijamy części uwierzytelniania i autoryzacji w tym przykładzie. W związku z tym ten punkt końcowy jest publicznie dostępny bez żadnych ograniczeń. Aby zapewnić bezpieczeństwo punktu końcowego negocjacji, należy zaimplementować odpowiednie mechanizmy uwierzytelniania i autoryzacji na podstawie określonych wymagań. Aby uzyskać wskazówki dotyczące ochrony punktów końcowych HTTP, zobacz następujące artykuły:
Następnie klienci mogą zażądać punktu końcowego https://<Your Function App Name>.azurewebsites.net/api/negotiate
funkcji, aby uzyskać adres URL usługi i token dostępu. Pełny przykład można znaleźć w witrynie GitHub.
Aby zapoznać się SignalRConnectionInfo
z przykładami powiązań wejściowych w innych językach, zobacz Powiązanie wejściowe usługi Azure Functions SignalR Service.
Własny punkt /negotiate
końcowy
Możesz również uwidocznić punkt końcowy negocjacji na własnym serwerze i samodzielnie zwrócić odpowiedź na negocjacje, jeśli używasz innych języków.
Korzystanie z parametru ConnectionString
Poniżej znajduje się pseudo kod w języku JavaScript pokazujący sposób implementowania punktu końcowego negocjacji dla centrum chat
i generowania tokenu dostępu z usługi Azure SignalR parametry połączenia.
import express from 'express';
const connectionString = '<your-connection-string>';
const hub = 'chat';
let app = express();
app.post('/chat/negotiate', (req, res) => {
let endpoint = /Endpoint=(.*?);/.exec(connectionString)[1];
let accessKey = /AccessKey=(.*?);/.exec(connectionString)[1];
let url = `${endpoint}/client/?hub=${hub}`;
let token = jwt.sign({ aud: url }, accessKey, { expiresIn: 3600 });
res.json({ url: url, accessToken: token });
});
app.listen(8080, () => console.log('server started'));
Następnie klient SignalR języka JavaScript łączy się z adresem URL /chat
:
let connection = new signalR.HubConnectionBuilder().withUrl('/chat').build();
connection.start();
Korzystanie z identyfikatora Entra firmy Microsoft
Usługa Azure SignalR udostępnia również interfejs API POST /api/hubs/${hub}/:generateToken?api-version=2022-11-01&userId=${userId}&minutesToExpire=${minutesToExpire}
REST do generowania tokenu dostępu klienta podczas korzystania z identyfikatora Entra firmy Microsoft.
Kroki to:
- Postępuj zgodnie z instrukcjamiDodaj przypisania ról, aby przypisać rolę
SignalR REST API Owner
lubSignalR Service Owner
tożsamość, aby tożsamość mogła wywołać interfejs API REST w celu wygenerowania tokenu dostępu klienta. - Pobieranie tokenu identyfikatora entra firmy Microsoft z zakresem przy użyciu biblioteki klienta tożsamości platformy Azure
https://signalr.azure.com/.default
- Użyj tego tokenu, aby odwiedzić interfejs API REST generowania tokenu
- Zwróć token dostępu klienta w odpowiedzi na negocjacje.
Poniżej znajduje się pseudokod w języku JavaScript pokazujący sposób implementowania punktu końcowego negocjacji dla centrum chat
i uzyskiwania tokenu dostępu przy użyciu identyfikatora Entra firmy Microsoft i interfejsu API /generateToken
REST.
import express from "express";
import axios from "axios";
import { DefaultAzureCredential } from "@azure/identity";
const endpoint = "https://<your-service>.service.signalr.net";
const hub = "chat";
const generateTokenUrl = `${endpoint}/api/hubs/${hub}/:generateToken?api-version=2022-11-01`;
let app = express();
app.get("/chat/negotiate", async (req, res) => {
// use DefaultAzureCredential to get the Entra ID token to call the Azure SignalR REST API
const credential = new DefaultAzureCredential();
const entraIdToken = await credential.getToken("https://signalr.azure.com/.default");
const token = (
await axios.post(generateTokenUrl, undefined, {
headers: {
"content-type": "application/json",
Authorization: `Bearer ${entraIdToken.token}`,
},
})
).data.token;
let url = `${endpoint}/client/?hub=${hub}`;
res.json({ url: url, accessToken: token });
});
app.listen(8080, () => console.log("server started"));
Następne kroki
Aby dowiedzieć się więcej na temat używania trybów domyślnych i bezserwerowych, zobacz następujące artykuły: