Clientverhandlung
Die erste Anforderung zwischen einem Client und einem Server ist die Aushandlungsanforderung. Wenn Sie eine selbstgehostete SignalR-Bibliothek verwenden, verwenden Sie die Anforderung, um eine Verbindung zwischen dem Client und dem Server herzustellen. Wenn Sie Azure SignalR Service verwenden, stellen Clients eine Verbindung mit dem Dienst her anstatt mit dem Anwendungsserver. In diesem Artikel werden Konzepte zu Aushandlungsprotokollen und Möglichkeiten zum Anpassen eines Aushandlungsendpunkts erläutert.
Wichtig
Unformatierte Verbindungszeichenfolgen werden in diesem Artikel nur zu Demonstrationszwecken angezeigt.
Eine Verbindungszeichenfolge enthält die Autorisierungsinformationen, die Ihre Anwendung für den Zugriff auf den Azure SignalR-Dienst benötigt. Der Zugriffsschlüssel in der Verbindungszeichenfolge ähnelt einem Stammkennwort für Ihren Dienst. Schützen Sie Ihre Zugriffsschlüssel in Produktionsumgebungen immer sorgfältig. Verwenden Sie Azure Key Vault, um Ihre Schlüssel sicher zu verwalten und zu rotieren, Ihre Verbindungszeichenfolge mithilfe von Microsoft Entra ID zu schützen und den Zugriff mit Microsoft Entra ID zu autorisieren.
Geben Sie Zugriffsschlüssel nicht an andere Benutzer weiter, vermeiden Sie das Hartcodieren, und speichern Sie die Schlüssel nicht als Klartext, auf den andere Benutzer Zugriff haben. Rotieren Sie die Schlüssel, wenn Sie glauben, dass sie möglicherweise gefährdet sind.
Die Antwort auf die POST [endpoint-base]/negotiate
-Anforderung enthält einen von drei Antworttypen:
Eine Antwort, die den
connectionId
-Wert enthält, der die Verbindung auf dem Server und die Liste der vom Server unterstützten Transporte identifiziert:{ "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" ] } ] }
Die von diesem Endpunkt zurückgegebenen Nutzdaten stellen die folgenden Daten bereit:
- Der
connectionId
-Wert wird von denLongPolling
- undServerSentEvents
-Transporten benötigt, um das Senden und Empfangen zu korrelieren. - Der
negotiateVersion
-Wert ist die Version des Aushandlungsprotokolls, die Sie zwischen dem Server und dem Client verwenden. Weitere Informationen finden Sie unter Transportprotokolle.-
negotiateVersion: 0
gibt nurconnectionId
zurück, und der Client sollte den WertconnectionId
alsid
in Verbindungsanforderungen verwenden. -
negotiateVersion: 1
gibtconnectionId
undconnectionToken
zurück, und der Client sollte den WertconnectionToken
alsid
in Verbindungsanforderungen verwenden.
-
- In der Liste
availableTransports
werden die vom Server unterstützten Transporte beschrieben. Für jeden Transport führen die Nutzdaten den Namen des Transports (transport
) und eine Liste der Übertragungsformate auf, die der Transport unterstützt (transferFormats
).
- Der
Eine Umleitungsantwort, die dem Client angibt, welche URL und (optional) Zugriffstoken als Ergebnis verwendet werden sollen:
{ "url": "https://<Server endpoint>/<Hub name>", "accessToken": "<accessToken>" }
Die von diesem Endpunkt zurückgegebenen Nutzdaten stellen die folgenden Daten bereit:
- Der
url
-Wert ist die URL, mit der der Client eine Verbindung herstellen soll. - Der
accessToken
-Wert ist ein optionales Bearertoken für den Zugriff auf die angegebene URL.
- Der
Eine Antwort, die einen
error
-Eintrag enthält, der den Verbindungsversuch beenden soll:{ "error": "This connection is not allowed." }
Die von diesem Endpunkt zurückgegebenen Nutzdaten stellen die folgenden Daten bereit:
- Die
error
-Zeichenfolge gibt Details dazu an, warum die Aushandlung fehlgeschlagen ist.
- Die
Wenn Sie Azure SignalR Service verwenden, stellen Clients eine Verbindung mit dem Dienst her anstatt mit dem App-Server. Das Einrichten von permanenten Verbindungen zwischen dem Client und Azure SignalR Service besteht aus drei Schritten:
Ein Client sendet eine Aushandlungsanforderung an den App-Server.
Der App-Server verwendet das Azure SignalR Service-SDK, um eine Umleitungsantwort zurückzugeben, die die Azure SignalR Service-URL und das Zugriffstoken enthält.
Für ASP.NET Core SignalR sieht eine typische Umleitungsantwort wie im folgenden Beispiel aus:
{ "url":"https://<SignalR name>.service.signalr.net/client/?hub=<Hub name>&...", "accessToken":"<accessToken>" }
Nachdem der Client die Umleitungsantwort empfangen hat, verwendet er die URL und das Zugriffstoken, um eine Verbindung mit SignalR Service herzustellen. Der Dienst leitet den Client dann an den App-Server weiter.
Wichtig
In einer selbstgehosteten SignalR-Bibliothek können einige Benutzer*innen die Clientaushandlung überspringen, wenn Clients nur WebSocket unterstützen und den Roundtrip für die Aushandlung speichern. Wenn Sie jedoch mit Azure SignalR Service arbeiten, sollten Clients immer einen vertrauenswürdigen Server oder ein vertrauenswürdiges Authentifizierungscenter bitten, das Zugriffstoken zu erstellen. Legen Sie daher SkipNegotiation
nicht auf true
auf der Clientseite fest.
SkipNegotiation
bedeutet, dass Clients das Zugriffstoken selbst erstellen müssen. Diese Einstellung birgt das Sicherheitsrisiko, dass der Client alles mit dem Dienstendpunkt machen könnte.
Sie können die Clientverbindung unterbrechen, um Einstellungen für Sicherheits- oder Geschäftsanforderungen anzupassen. Zum Beispiel:
- Verwenden Sie einen kurzen
AccessTokenLifetime
-Wert für die Sicherheit. - Übergeben Sie nur erforderliche Informationen aus Clientansprüchen.
- Fügen Sie benutzerdefinierte Ansprüche für geschäftliche Anforderungen hinzu.
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);
});
Wenn Sie über mehrere App-Server verfügen, gibt es keine Garantie (standardmäßig), dass der Server, der aushandelt, und der Server, der den Hubaufruf abruft, identisch sind. In einigen Fällen sollten Informationen zum Clientstatus lokal auf dem App-Server verwaltet werden.
Wenn Sie z. B. Blazor auf der Serverseite verwenden, wird der UI-Zustand auf der Serverseite verwaltet. Daher möchten Sie, dass alle Clientanforderungen zum selben Server wechseln, einschließlich der SignalR-Verbindung. Anschließend müssen Sie Required
für den Modus „Serverbindung“ während der Aushandlung aktivieren:
services.AddSignalR().AddAzureSignalR(options => {
options.ServerStickyMode = ServerStickyMode.Required;
});
Eine andere Möglichkeit, die Aushandlung anzupassen, besteht in mehreren Endpunkten. Da der App-Server die Dienst-URL als Aushandlungsantwort bereitstellt, kann der App-Server bestimmen, welcher Endpunkt für die Effizienz des Lastenausgleichs und der Kommunikation an Clients zurückgegeben werden soll. Das heißt, Sie können zulassen, dass der Client eine Verbindung mit dem nächstgelegenen Dienstendpunkt herstellt, um Kosten für den Datenverkehr zu sparen.
// 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
}
}
Registrieren Sie den Router auch für die Abhängigkeitsinjektion.
Unformatierte Verbindungszeichenfolgen werden in diesem Artikel nur zu Demonstrationszwecken angezeigt. Schützen Sie Ihre Zugriffsschlüssel in Produktionsumgebungen immer sorgfältig. Verwenden Sie Azure Key Vault, um Ihre Schlüssel sicher zu verwalten und zu rotieren, Ihre Verbindungszeichenfolge mithilfe von Microsoft Entra ID zu schützen und den Zugriff mit Microsoft Entra ID zu autorisieren.
// 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>")
};
});
Im serverlosen Modus (Serverless
) akzeptiert kein Server SignalR-Clients. Um Ihre Verbindungszeichenfolge zu schützen, müssen Sie SignalR-Clients vom Aushandlungsendpunkt an Azure SignalR Service umleiten, anstatt Ihre Verbindungszeichenfolge an alle SignalR-Clients zu übergeben.
Die bewährte Methode besteht darin, einen Aushandlungsendpunkt zu hosten. Anschließend können Sie SignalR-Clients für diesen Endpunkt verwenden und die Dienst-URL und das Zugriffstoken abrufen.
Sie können die Aushandlung angehen, indem Sie mit dem Management-SDK arbeiten.
Sie können die Instanz von ServiceHubContext
verwenden, um die Endpunkt-URL und das entsprechende Zugriffstoken für SignalR-Clients zu generieren und eine Verbindung mit Azure SignalR Service herzustellen:
var negotiationResponse = await serviceHubContext.NegotiateAsync(new (){ UserId = "<Your User Id>" });
Angenommen, Ihr Hubendpunkt ist http://<Your Host Name>/<Your Hub Name>
. Dann ist Ihr Aushandlungsendpunkt http://<Your Host Name>/<Your Hub Name>/negotiate
. Nachdem Sie den Aushandlungsendpunkt gehostet haben, können Sie die SignalR-Clients verwenden, um eine Verbindung mit Ihrem Hub herzustellen:
var connection = new HubConnectionBuilder().WithUrl("http://<Your Host Name>/<Your Hub Name>").Build();
await connection.StartAsync();
Ein vollständiges Beispiel zur Verwendung des Management-SDK zum Umleiten von SignalR-Clients zu Azure SignalR Service finden Sie auf GitHub.
Wenn Sie eine Azure-Funktions-App verwenden, können Sie mit der Funktionserweiterung arbeiten. Im Folgenden finden Sie ein Beispiel für die Verwendung von SignalRConnectionInfo
im isolierten C#-Arbeitsmodell, das Ihnen beim Erstellen der Aushandlungsantwort hilft:
[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;
}
Warnung
Aus Gründen der Einfachheit werden die Authentifizierungs- und Autorisierungsteile in diesem Beispiel weggelassen. Daher ist dieser Endpunkt ohne Einschränkungen öffentlich zugänglich. Um die Sicherheit Ihres Aushandlungsendpunkts sicherzustellen, sollten Sie geeignete Authentifizierungs- und Autorisierungsmechanismen basierend auf Ihren spezifischen Anforderungen implementieren. Anleitungen zum Schutz Ihrer HTTP-Endpunkte finden Sie in den folgenden Artikeln:
Anschließend können Ihre Clients den Funktionsendpunkt https://<Your Function App Name>.azurewebsites.net/api/negotiate
anfordern, um die Dienst-URL und das Zugriffstoken abzurufen. Ein vollständiges Beispielprojekt finden Sie auf GitHub.
Informationen zu SignalRConnectionInfo
Eingabebindungsbeispielen in anderen Sprachen finden Sie unter Eingabebindung des Azure Functions SignalR Service.
Sie können den Aushandlungsendpunkt auch auf Ihrem eigenen Server verfügbar machen und die Aushandlungsantwort selbst zurückgeben, wenn Sie andere Sprachen verwenden.
Nachfolgend finden Sie einen Pseudocode in JavaScript, der anzeigt, wie der Aushandlungsendpunkt für den Hub chat
implementiert und ein Zugriffstoken aus der Azure SignalR-Verbindungszeichenfolge generiert wird.
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'));
Ein JavaScript SignalR-Client stellt dann eine Verbindung mit der URL /chat
her:
let connection = new signalR.HubConnectionBuilder().withUrl('/chat').build();
connection.start();
Azure SignalR stellt auch die REST-API POST /api/hubs/${hub}/:generateToken?api-version=2022-11-01&userId=${userId}&minutesToExpire=${minutesToExpire}
bereit, um das Zugriffstoken des Clients für Sie zu generieren, wenn Sie Microsoft Entra ID verwenden.
Führen Sie die folgenden Schritte durch:
- Befolgen Sie die Schritte unter Hinzufügen von Rollenzuweisungen, um Ihrer Identität die Rolle
SignalR REST API Owner
oderSignalR Service Owner
zuzuweisen, damit Ihre Identität über die Berechtigung verfügt, die REST-API aufzurufen, um das Zugriffstoken des Clients zu generieren. - Verwenden der Azure Identity-Clientbibliothek zum Abrufen des Microsoft Entra ID-Tokens mit dem Bereich
https://signalr.azure.com/.default
- Verwenden dieses Tokens, um die REST-API zum Generieren von Token anzuzeigen
- Geben Sie das Zugriffstoken des Clients in der Aushandlungsantwort zurück.
Nachfolgend finden Sie einen Pseudocode in JavaScript, der anzeigt, wie der Aushandlungsendpunkt für den Hub chat
implementiert und ein Zugriffstoken mithilfe von Microsoft Entra ID und der REST-API /generateToken
abgerufen wird.
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"));
In den folgenden Artikeln finden Sie weitere Informationen zur Verwendung der standardmäßigen und serverlosen Modi: