Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel wird beschrieben, wie Sie Benutzer in ASP.NET Core Anwendungen mit SignalR authentifizieren und autorisieren.
Authentifizieren von Benutzern, die eine Verbindung mit einem SignalR-Hub herstellen
SignalR kann mit ASP.NET Core-Authentifizierung verwendet werden, um jeder Verbindung einen Benutzer zuzuordnen. In einem Hub kann über die HubConnectionContext.User-Eigenschaft auf Authentifizierungsdaten zugegriffen werden. Authentifizierung ermöglicht es dem Hub, Methoden für alle Verbindungen aufzurufen, die einem Benutzer zugeordnet sind. Weitere Informationen finden Sie unter Verwalten von Benutzern und Gruppen in SignalR. Mehrere Verbindungen können einem einzelnen Benutzer zugeordnet werden.
Der folgende Code ist ein Beispiel, das SignalR und ASP.NET Core-Authentifizierung verwendet:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SignalRAuthenticationSample.Data;
using SignalRAuthenticationSample.Hubs;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapHub<ChatHub>("/chat");
app.Run();
Note
Wenn ein Token während der Lebensdauer einer Verbindung abläuft, funktioniert die Verbindung standardmäßig weiterhin.
LongPolling- und ServerSentEvents-Verbindungen schlagen bei nachfolgenden Anforderungen fehl, wenn sie keine neuen Zugriffstoken senden. Damit Verbindungen geschlossen werden, wenn das Authentifizierungstoken abläuft, legen Sie die Option CloseOnAuthenticationExpiration fest.
Benutzer- und Rollenänderungen während der Verbindungslebensdauer
SignalR erfasst den authentifizierten Benutzer, wenn eine Verbindung hergestellt wird, und speichert sie für die Lebensdauer der Verbindung zwischen. Der zwischengespeicherte Prinzipal steht Hubmethoden über die Eigenschaft Context.User (HubCallerContext.User) zur Verfügung und wird verwendet, um Aufrufe von Hubmethoden zu autorisieren.
SignalR aktualisiert den Benutzer während der Lebensdauer der Verbindung nicht automatisch, unabhängig vom Authentifizierungsschema. Dieses Verhalten gilt für alle Schemas, einschließlich cookie Authentifizierung und Bearertokenauthentifizierung.
Änderungen an der Identität, den Rollen oder den Claims eines Benutzers, die nach dem Herstellen der Verbindung auftreten, werden in einer bestehenden Verbindung nicht berücksichtigt. Dieses Verhalten gilt auch dann, wenn der zugrunde liegende Transport neue HTTP-Anforderungen wie z. B. die LongPolling und ServerSentEvents transporte vorschreibt. Obwohl die ASP.NET Core-Authentifizierungsmiddleware jede dieser HTTP-Anforderungen erneut authentifiziert, verwendet SignalR weiterhin den Prinzipal, der beim Herstellen der Verbindung zwischengespeichert wurde.
Betrachten Sie das folgende Szenario:
- Eine App verwendet die cookie-Authentifizierung und den
LongPolling-Transport. Das gleiche Verhalten gilt für die Bearertokenauthentifizierung und denServerSentEventsTransport. - Ein Benutzer ist mit der
EditorRolle angemeldet und verfügt über eine offene SignalR Verbindung. - Die App entfernt die
EditorRolle von diesem Benutzer.
Bei der nächsten Anforderung mit langer Abrufzeit kann die Authentifizierungsmiddleware den Benutzer ohne die Rolle Editor authentifizieren. Dies kann beispielsweise passieren, wenn Rollen aus einem Datenspeicher geladen werden oder das cookie aktualisiert wird. Der Hub autorisiert die Hubmethodenaufrufe des Benutzers für [Authorize(Roles = "Editor")] jedoch weiterhin, da Context.User (HubCallerContext.User) weiterhin den Prinzipal enthält, der vor dem Entfernen der Rolle zwischengespeichert wurde. Der Nutzer kann weiterhin ausschließlich Hubmethoden für Editor aufrufen, bis die Verbindung geschlossen wird.
Um eine aktualisierte Autorisierung für eine aktive Verbindung zu erzwingen, führen Sie einen der folgenden Ansätze aus:
- Schließen Sie betroffene Verbindungen, sodass Clients erneut eine Verbindung herstellen und erneut authentifizieren können. Bei der Bearertokenauthentifizierung schließt die CloseOnAuthenticationExpiration-Option Verbindungen, wenn das Authentifizierungstoken abläuft.
- Führen Sie Autorisierungsprüfungen in Hubmethoden für aktuelle Daten durch, z. B. die aktuellen Rollen oder Ansprüche des Benutzers aus einem Datenspeicher, anstatt nur auf den zwischengespeicherten Prinzipal zu vertrauen.
In .NET 11 und höher kann ein Client die Anmeldeinformationen für eine aktive Verbindung aktualisieren, ohne erneut eine Verbindung herzustellen. Wenn der Client ein aktualisiertes Token vorlegt, authentifiziert der Server es erneut und ersetzt das zwischengespeicherte Context.User an Ort und Stelle, sodass spätere Aufrufe von Hubmethoden anhand der aktualisierten Rollen und Claims autorisiert werden. Der aktualisierte Prinzipal muss demselben SignalR Benutzer zugeordnet werden, sodass eine Aktualisierung Rollen und Ansprüche aktualisiert, die Benutzeridentität oder das Routing der Verbindung jedoch nicht ändert. Weitere Informationen finden Sie unter Authentifizierungsaktualisierung.
Cookie-Authentifizierung
In einer browserbasierten App ermöglicht cookie-Authentifizierung, dass vorhandene Benutzeranmeldeinformationen automatisch in SignalR-Verbindungen einfließen. Wenn der Browserclient verwendet wird, ist keine zusätzliche Konfiguration erforderlich. Wenn der Benutzer bei einer App angemeldet ist, erbt die SignalR Verbindung diese Authentifizierung automatisch.
Cookies sind eine browser-spezifische Möglichkeit, Zugriffstoken zu senden, aber auch Nicht-Browser-Clients können sie senden. Wenn der .NET Client verwendet wird, kann die eigenschaft Cookies im aufruf .WithUrl konfiguriert werden, um einen cookie bereitzustellen. Die Verwendung von cookie-Authentifizierung über den .NET-Client erfordert jedoch, dass die App eine API zum Austauschen von Authentifizierungsdaten für ein cookie bereitstellt.
Von Bedeutung
Ab ASP.NET Core 10 leiten bekannte API-Endpunkte bei Nutzung der cookie-Authentifizierung nicht mehr zu Anmeldeseiten um. Stattdessen geben sie 401/403-Statuscodes zurück. Ausführliche Informationen finden Sie unter API-Endpunktauthentifizierungsverhalten in ASP.NET Core.
Bearer Tokenauthentifizierung
Der Client kann ein Zugriffstoken bereitstellen, anstatt ein cookie zu verwenden. Der Server überprüft das Token und verwendet es zum Identifizieren des Benutzers. Bei Datentransporten, die mehrere HTTP-Anforderungen stellen (z. B. LongPolling und ServerSentEvents), wird die Authentifizierung bei jeder Anforderung ausgeführt, aber SignalR speichert den resultierenden Prinzipal für die Dauer der Verbindung im Zwischenspeicher. Wie bei der Authentifizierung mit cookie führt SignalR während der Verbindungsdauer keine automatische erneute Validierung des Benutzers durch, um zu prüfen, ob das Token widerrufen wurde oder ob sich die Rollen oder Claims des Benutzers geändert haben. Weitere Informationen finden Sie unter Benutzer- und Rollenänderungen während der Lebensdauer der Verbindung.
Im JavaScript-Client kann das Token mithilfe der AccessTokenFactory-Option bereitgestellt werden.
// Connect, using the token we got.
this.connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat", { accessTokenFactory: () => this.loginToken })
.build();
Im .NET-Client gibt es eine ähnliche AccessTokenProvider-Eigenschaft, die zum Konfigurieren des Tokens verwendet werden kann:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(_myAccessToken);
})
.Build();
Note
Die Zugriffstokenfunktion wird vor jeder HTTP-Anfrage aufgerufen, die von SignalR ausgeführt wird. Wenn das Token erneuert werden muss, um die Verbindung aktiv zu halten, führen Sie die Erneuerung aus dieser Funktion aus, und geben Sie das aktualisierte Token zurück. Das Token muss möglicherweise erneuert werden, damit es während der Verbindung nicht abläuft.
In Standardweb-APIs werden Bearertoken in einem HTTP-Header gesendet. SignalR kann diese Header jedoch in Browsern nicht setzen, wenn einige Transportmethoden verwendet werden. Wenn WebSockets und Server-Sent Events verwendet werden, wird das Token als Query-String-Parameter übertragen.
JWT Integrierte Authentifizierung
Auf dem Server wird die Bearertokenauthentifizierung mithilfe der JSON-Webtoken-Middleware (JWT) Bearerkonfiguriert:
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using SignalRAuthenticationSample.Data;
using SignalRAuthenticationSample.Hubs;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using SignalRAuthenticationSample;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthentication(options =>
{
// Identity made Cookie authentication the default.
// However, we want JWT Bearer Auth to be the default.
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
// Configure the Authority to the expected value for
// the authentication provider. This ensures the token
// is appropriately validated.
options.Authority = "Authority URL"; // TODO: Update URL
// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or
// Server-Sent Events request comes in.
// Sending the access token in the query string is required when using WebSockets or ServerSentEvents
// due to a limitation in Browser APIs. We restrict it to only calls to the
// SignalR hub in this code.
// See https://docs.microsoft.com/aspnet/core/signalr/security#access-token-logging
// for more information about security considerations when using
// the query string to transmit the access token.
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
// Change to use Name as the user identifier for SignalR
// WARNING: This requires that the source of your JWT token
// ensures that the Name claim is unique!
// If the Name claim isn't unique, users could receive messages
// intended for a different user!
builder.Services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
// Change to use email as the user identifier for SignalR
// builder.Services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();
// WARNING: use *either* the NameUserIdProvider *or* the
// EmailBasedUserIdProvider, but do not use both.
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
Note
Die Abfragezeichenfolge wird in Browsern verwendet, wenn eine Verbindung mit WebSockets und Server-Sent Events aufgrund von Browser-API-Einschränkungen hergestellt wird. Wenn Sie HTTPS verwenden, sichert die TLS-Verbindung die Abfragezeichenfolgenwerte. Viele Server protokollieren jedoch Abfragezeichenfolgenwerte. Weitere Informationen finden Sie unter Sicherheitsüberlegungen zu ASP.NET CoreSignalR. SignalR verwendet Header, um Token in Umgebungen zu übertragen, die sie unterstützen, z. B. die .NET und Java Clients.
IdentityServerauthentifizierung JWT
Fügen Sie bei Verwendung von Duende IdentityServer dem Projekt einen PostConfigureOptions<TOptions> Dienst hinzu:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{
public void PostConfigure(string name, JwtBearerOptions options)
{
var originalOnMessageReceived = options.Events.OnMessageReceived;
options.Events.OnMessageReceived = async context =>
{
await originalOnMessageReceived(context);
if (string.IsNullOrEmpty(context.Token))
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
}
};
}
}
Registrieren Sie den Dienst nach dem Hinzufügen von Diensten für die Authentifizierung (mit der AddAuthentication Methode) und dem Authentifizierungshandler für Identity Server (mit der AddIdentityServerJwt Methode):
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection.Extensions;
using SignalRAuthenticationSample.Hubs;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,
ConfigureJwtBearerOptions>());
builder.Services.AddRazorPages();
var app = builder.Build();
// Code removed for brevity.
Authentifizierungsaktualisierung
Eine SignalR Verbindung kann länger bestehen als das Zugriffstoken, durch das sie hergestellt wurde. Wenn CloseOnAuthenticationExpiration aktiviert ist, schließt der Server die Verbindung nach Ablauf des Tokens, und der Client muss die Verbindung erneut herstellen, um fortzufahren. Nachrichten, die während der Unterbrechung gesendet werden, gehen verloren, und das Gruppen- und Benutzerrouting sind gestört, bis sich der Client wieder verbindet.
Durch die Aktualisierung der Authentifizierung, die in .NET 11 und höher verfügbar ist, kann ein Client die Anmeldeinformationen für eine aktive Verbindung aktualisieren, ohne erneut eine Verbindung herzustellen. Der Server authentifiziert die Anforderung zur Aktualisierung über die normale Autorisierungspipeline des Endpunkts erneut und ersetzt dabei das ClaimsPrincipal der Verbindung direkt, solange der aktualisierte Prinzipal demselben SignalR-Benutzer zugeordnet ist.
Aktivieren der Authentifizierungsaktualisierung auf dem Server
Aktivieren Sie die Authentifizierungsaktualisierung in den Optionen des Hubs MapHub durch Festlegen EnableAuthenticationRefresh auf true. Aktivieren Sie sie zusammen mit CloseOnAuthenticationExpiration, damit eine Verbindung, deren Token abläuft, ohne rechtzeitig erneuert zu werden, geschlossen wird, anstatt mit veralteten Anmeldeinformationen offen zu bleiben:
app.MapHub<ChatHub>("/chat", options =>
{
options.CloseOnAuthenticationExpiration = true;
options.EnableAuthenticationRefresh = true;
});
Wenn die Authentifizierungsaktualisierung aktiviert ist und das Authentifizierungsticket einen Ablauf hat, meldet die Aushandlungsantwort die verbleibende Tokenlebensdauer, damit der Client Aktualisierungen planen kann.
Um eine Aktualisierung zu prüfen oder abzulehnen, legen Sie den OnAuthenticationRefresh Rückruf fest. Sie wird ausgeführt, nachdem die Aktualisierungsanforderung authentifiziert wurde, aber bevor der Benutzer der Verbindung ersetzt wird. Kehren Sie false zurück, um die Aktualisierung abzulehnen, in diesem Fall antwortet der Endpunkt mit einem HTTP 403-Statuscode, und die Verbindung behält den aktuellen Benutzer bei. Der Callback ist eine zusätzliche Prüfung zusätzlich zur integrierten Verifizierung, dass der aktualisierte Prinzipal demselben SignalR Benutzer zugeordnet ist. Sie kann eine Aktualisierung ablehnen, aber sie kann eine nicht genehmigen, die die integrierte Überprüfung fehlschlägt:
app.MapHub<ChatHub>("/chat", options =>
{
options.CloseOnAuthenticationExpiration = true;
options.EnableAuthenticationRefresh = true;
options.OnAuthenticationRefresh = context =>
{
if (!context.NewUser.HasClaim("tenant", "contoso"))
{
return Task.FromResult(false);
}
return Task.FromResult(true);
};
});
Der aktualisierte Prinzipal muss demselben SignalR Benutzer zugeordnet sein wie die Verbindung. Wenn sie einer anderen Benutzer-ID zugeordnet ist, wird die Aktualisierung abgelehnt: Der Endpunkt antwortet mit einem HTTP 403-Statuscode, und die Verbindung behält den aktuellen Benutzer bei und bleibt verbunden. Eine Aktualisierung ändert niemals die Context.UserIdentifier der Verbindung und leitet auch keine Nachrichten um, die mit Clients.User gesendet wurden, selbst bei einer erfolgreichen Aktualisierung. Der Routingbezeichner wird beim Verbindungsaufbau festgelegt. Um ihn zu ändern, verbinden Sie den Client erneut.
Um festzulegen, wie weit eine Aktualisierung die Ablaufzeit der Authentifizierung einer Verbindung verlängern kann, setzen Sie MaximumAuthenticationExpiration. Die aktualisierte Ablaufzeit ist auf höchstens diese Zeitspanne ab dem aktuellen Zeitpunkt begrenzt, auch wenn das Token eine längere Lebensdauer angibt. Diese Begrenzung gilt immer dann, wenn die Aktualisierung der Authentifizierung aktiviert ist, auch wenn das Token selbst kein Ablaufdatum festlegt. In diesem Fall wird für die Verbindung eine bekannte Ablaufzeit festgelegt, sodass die Antwort auf die Aushandlung die Gültigkeitsdauer des Tokens angibt und der Client automatische Aktualisierungen einplanen kann. Der Wert muss größer als Null sein und gilt nicht für Windows-Authentifizierung, der nie nachverfolgt oder aktualisiert wird.
Aktualisieren der Authentifizierung vom .NET-Client
Der .NET-Client aktualisiert Anmeldeinformationen mithilfe von AccessTokenProvider, das für die Verbindung konfiguriert ist. Bei jedem Aktualisierungsaufruf AccessTokenProvider wird ein neues Zugriffstoken abgerufen, anstatt das token wieder zu verwenden, das beim Starten der Verbindung zwischengespeichert wurde.
Zum expliziten Aktualisieren rufen Sie RefreshAuthenticationAsync auf, die die vom Server gemeldete neue Token-Lebensdauer zurückgibt:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chat", options =>
{
options.AccessTokenProvider = GetAccessTokenAsync;
})
.Build();
await connection.StartAsync();
TimeSpan? newLifetime = await connection.RefreshAuthenticationAsync();
Um das Token automatisch zu aktualisieren, bevor es abläuft, rufen Sie WithAuthenticationRefresh auf und konfigurieren Sie AuthenticationRefreshOptions:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chat", options =>
{
options.AccessTokenProvider = GetAccessTokenAsync;
})
.WithAuthenticationRefresh(options =>
{
options.RefreshBeforeExpiration = TimeSpan.FromMinutes(2);
})
.Build();
AuthenticationRefreshOptions stellt die folgenden Einstellungen bereit:
-
EnableAutoRefresh: Aktiviert die automatische Aktualisierung, bevor das Token abläuft. Wird standardmäßig auftruefestgelegt. Der Client plant eine Aktualisierung nur dann, wenn der Server eine Lebensdauer des Tokens meldet. Wenn der Server keine Lebensdauer meldet, wird keine automatische Aktualisierung geplant undRefreshAuthenticationAsynckann weiterhin manuell aufgerufen werden. -
RefreshBeforeExpiration: Wie lange vor dem gemeldeten Ablauf aktualisiert werden soll. Die Standardeinstellung beträgt fünf Minuten.
Um Aktualisierungen zu beobachten, behandeln Sie die Ereignisse AuthenticationRefreshFailed und AuthenticationRefreshed auf der Verbindung. Sowohl automatische als auch manuelle Aktualisierungen lösen die folgenden Ereignisse aus:
connection.AuthenticationRefreshed += context =>
{
Console.WriteLine(
$"Authentication refreshed. New lifetime: {context.NewTokenLifetime}");
return Task.CompletedTask;
};
connection.AuthenticationRefreshFailed += context =>
{
Console.WriteLine(
$"Authentication refresh failed: {context.Exception}");
return Task.CompletedTask;
};
Aktualisieren der Authentifizierung vom JavaScript-Client
Der JavaScript-Client aktualisiert Anmeldeinformationen mithilfe der accessTokenFactory, die für die Verbindung konfiguriert ist. Jede Aktualisierung ruft accessTokenFactory auf, um ein neues Zugriffstoken abzurufen.
Um explizit zu aktualisieren, rufen Sie refreshAuthentication auf, das mit der neuen, vom Server gemeldeten Token-Lebensdauer in Sekunden aufgelöst wird:
const newLifetimeInSeconds = await connection.refreshAuthentication();
Um das Token automatisch zu erneuern, bevor es abläuft, rufen Sie IAuthenticationRefreshOptions auf und konfigurieren Sie optional withAuthenticationRefresh. Aktualisierungsergebnisse mit onAuthenticationRefreshed und onAuthenticationRefreshFailed verarbeiten:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chat", {
accessTokenFactory: () => getAccessToken()
})
.withAuthenticationRefresh({
refreshBeforeExpirationInMilliseconds: 120000
})
.build();
connection.onAuthenticationRefreshed(context => {
console.log(
`Authentication refreshed. New lifetime: ${context.newTokenLifetimeInSeconds}`);
});
connection.onAuthenticationRefreshFailed(context => {
console.log(`Authentication refresh failed: ${context.error}`);
});
IAuthenticationRefreshOptions stellt die folgenden Einstellungen bereit:
-
enableAutoRefresh: Aktiviert die automatische Aktualisierung, bevor das Token abläuft. Wird standardmäßig auftruefestgelegt. Wie beim .NET-Client wird die automatische Aktualisierung nur geplant, wenn der Server eine Tokenlebensdauer meldet. -
refreshBeforeExpirationInMilliseconds: Wie lange vor dem gemeldeten Ablauf aktualisiert werden soll, in Millisekunden. Der Standardwert beträgt 300.000 (fünf Minuten).
Reagieren auf eine Aktualisierung im Hub
Überschreiben Sie OnAuthenticationRefreshedAsync im Hub, um Code auszuführen, nachdem der aktualisierte Sicherheitsprinzipal auf die Verbindung angewendet worden ist.
Context.User spiegelt den aktualisierten Prinzipal wider. Wie zuvor beschrieben, ändern sich das Context.UserIdentifier- und SignalR-Benutzer-Routing bei einer Aktualisierung nicht:
public class ChatHub : Hub
{
public override Task OnAuthenticationRefreshedAsync()
{
return Clients.Caller.SendAsync(
"AuthenticationRefreshed", Context.UserIdentifier);
}
}
Eine Hubmethode, die bereits ausgeführt wird, behält den Context.User bei, mit dem sie gestartet wurde. Spätere Aufrufe verwenden das aktualisierte Context.User. Weitere Informationen dazu, wie SignalR den authentifizierten Benutzer zwischenspeichert, finden Sie unter Benutzer- und Rollenänderungen während der Verbindungsdauer.
Für die Authentifizierungsaktualisierung ist eine Verbindung erforderlich, die die Protokollversion 1 oder höher ausgehandelt hat. Die automatische Aktualisierung wird nur geplant, wenn der Server eine Tokenlebensdauer meldet, die in der Regel aus einem Authentifizierungsschema stammt, das einen Ablauf festlegt, z. B. Bearertoken. Windows-Authentifizierung meldet keinen Ablauf und wird von diesem Feature nicht nachverfolgt oder aktualisiert.
Cookies gegenüber Bearer-Token
Cookies sind spezifisch für Browser. Das Senden von Authentifizierungs-Token über andere Arten von Clients erhöht die Komplexität im Vergleich zum Versenden von Bearer-Token. Cookie-Authentifizierung wird nur empfohlen, wenn die App nur Benutzer über den Browserclient authentifizieren muss. Bearer Die Tokenauthentifizierung ist der empfohlene Ansatz bei der Verwendung anderer Clients als des Browserclients.
Windows-Authentifizierung
Wenn Windows-Authentifizierung in der App konfiguriert ist, kann SignalR diese Identität zum Sichern von Hubs verwenden. Fügen Sie jedoch einen benutzerdefinierten Benutzer-ID-Anbieter hinzu, um Nachrichten an einzelne Benutzer zu senden. Das Windows-Authentifizierungssystem stellt den Anspruch „Namenbezeichner“ nicht bereit. SignalR verwendet den Anspruch, um den Benutzernamen zu bestimmen.
Fügen Sie eine neue Klasse hinzu, die IUserIdProvider implementiert, und rufen Sie einen der Ansprüche des Benutzers ab, um ihn als Bezeichner zu verwenden. Um beispielsweise den Anspruch „Name“ zu verwenden (der Windows-Benutzername im Format [Domain]/[Username]), erstellen Sie die folgende Klasse:
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
Verwenden Sie anstelle von ClaimTypes.Name einen beliebigen Wert aus dem User, z. B. den Windows SID-Bezeichner usw.
Note
Der angegebene Wert muss für alle Benutzer im System eindeutig sein. Andernfalls erreicht eine nachricht, die für einen Benutzer vorgesehen ist, möglicherweise einen anderen Benutzer.
Registrieren Sie diese Komponente in der Program.cs Datei:
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.SignalR;
using SignalRAuthenticationSample;
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddRazorPages();
services.AddSignalR();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
var app = builder.Build();
// Code removed for brevity.
Im .NET Client muss Windows-Authentifizierung aktiviert sein, indem die Eigenschaft UseDefaultCredentials festgelegt wird:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.UseDefaultCredentials = true;
})
.Build();
Windows-Authentifizierung wird in Microsoft Edge unterstützt, aber nicht in allen Browsern. In Chrome und Safari schlägt der Versuch beispielsweise fehl, Windows-Authentifizierung und WebSockets zu verwenden. Wenn Windows-Authentifizierung fehlschlägt, versucht der Client, auf andere Transporte zurückzugreifen, was möglicherweise funktioniert.
Ansprüche nutzen, um die Identitätsverwaltung anzupassen
Eine App, die Benutzer authentifiziert, kann SignalR-Benutzer-IDs von Benutzeransprüchen ableiten. Implementieren Sie SignalR, und registrieren Sie die Implementierung, um anzugeben, wie IUserIdProvider Benutzer-IDs erstellt.
Der Beispielcode zeigt, wie Sie mithilfe von Ansprüchen die E-Mail-Adresse des Benutzers als identifizierende Eigenschaft auswählen.
Note
Der angegebene Wert muss für alle Benutzer im System eindeutig sein. Andernfalls erreicht eine nachricht, die für einen Benutzer vorgesehen ist, möglicherweise einen anderen Benutzer.
public class EmailBasedUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value!;
}
}
Die Kontoregistrierung fügt der ASP.NET Identitätsdatenbank einen Anspruch mit typ ClaimsTypes.Email hinzu.
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
.ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
// Add the email claim and value for this user.
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Email, Input.Email));
// Remaining code removed for brevity.
Registrieren Sie diese Komponente in der Program.cs Datei:
builder.Services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();
Autorisieren von Benutzern für den Zugriff auf Hubs und Hubmethoden
Standardmäßig kann ein nicht authentifizierter Benutzer alle Methoden in einem Hub aufrufen. Wenden Sie das AuthorizeAttribute-Attribut auf den Hub an, um Authentifizierung zu verlangen:
[Authorize]
public class ChatHub: Hub
{
}
Die Konstruktorargumente und -eigenschaften des [Authorize]-Attributs können verwendet werden, um den Zugriff auf Benutzer zu beschränken, die bestimmten Autorisierungsrichtlinien entsprechen. Mit der benutzerdefinierten Autorisierungsrichtlinie namens MyAuthorizationPolicy können beispielsweise nur Benutzer, die dieser Richtlinie entsprechen, mithilfe des folgenden Codes auf den Hub zugreifen:
[Authorize("MyAuthorizationPolicy")]
public class ChatPolicyHub : Hub
{
public override async Task OnConnectedAsync()
{
await Clients.All.SendAsync("ReceiveSystemMessage",
$"{Context.UserIdentifier} joined.");
await base.OnConnectedAsync();
}
// Code removed for brevity.
Das [Authorize]-Attribut kann auf einzelne Hubmethoden angewendet werden. Wenn der aktuelle Benutzer nicht der für die Methode geltenden Richtlinie entspricht, wird ein Fehler an den Aufrufer zurückgegeben:
[Authorize]
public class ChatHub : Hub
{
public async Task Send(string message)
{
// ... Send a message to all users ...
}
[Authorize("Administrators")]
public void BanUser(string userName)
{
// ... Ban a user from the chat room (something only Administrators can do) ...
}
}
Verwenden von Autorisierungshandlern zum Anpassen der Hubmethodenautorisierung
SignalR bietet eine benutzerdefinierte Ressource für Autorisierungshandler, wenn eine Hubmethode Autorisierung erfordert. Die Ressource ist eine Instanz von HubInvocationContext.
HubInvocationContext enthält HubCallerContext, den Namen der aufgerufenen Hubmethode, sowie die Argumente für die Hubmethode.
Betrachten Sie das Beispiel eines Chatrooms, der es mehreren Organisationen ermöglicht, sich über Microsoft Entra ID anzumelden. Jede Person mit einem Microsoft-Konto kann sich beim Chat anmelden, aber nur Mitglieder der besitzenden Organisation sollte in der Lage sein, Benutzer zu sperren oder den Chatverlauf anderer Benutzer anzuzeigen. Es kann auch erforderlich sein, einige Funktionen von bestimmten Benutzern einzuschränken. Beachten Sie in diesem Szenario, wie DomainRestrictedRequirement als benutzerdefiniertes IAuthorizationRequirement dient. Da der HubInvocationContext Ressourcenparameter übergeben wird, kann die interne Logik den Kontext prüfen, in dem der Hub aufgerufen wird, und Entscheidungen treffen, mit denen der Benutzer einzelne Hubmethoden ausführen kann:
[Authorize]
public class ChatHub : Hub
{
public void SendMessage(string message)
{
}
[Authorize("DomainRestricted")]
public void BanUser(string username)
{
}
[Authorize("DomainRestricted")]
public void ViewUserHistory(string username)
{
}
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
namespace SignalRAuthenticationSample;
public class DomainRestrictedRequirement :
AuthorizationHandler<DomainRestrictedRequirement, HubInvocationContext>,
IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
DomainRestrictedRequirement requirement,
HubInvocationContext resource)
{
if (context.User.Identity != null &&
!string.IsNullOrEmpty(context.User.Identity.Name) &&
IsUserAllowedToDoThis(resource.HubMethodName,
context.User.Identity.Name) &&
context.User.Identity.Name.EndsWith("@microsoft.com"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
private bool IsUserAllowedToDoThis(string hubMethodName,
string currentUsername)
{
return !(currentUsername.Equals("asdf42@microsoft.com") &&
hubMethodName.Equals("banUser", StringComparison.OrdinalIgnoreCase));
}
}
Fügen Sie in der Program.cs Datei die neue Richtlinie hinzu, die die benutzerdefinierte DomainRestrictedRequirement Anforderung als Parameter zum Erstellen der DomainRestricted Richtlinie bereitstellt:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SignalRAuthenticationSample;
using SignalRAuthenticationSample.Data;
using SignalRAuthenticationSample.Hubs;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
var services = builder.Services;
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthorization(options =>
{
options.AddPolicy("DomainRestricted", policy =>
{
policy.Requirements.Add(new DomainRestrictedRequirement());
});
});
services.AddRazorPages();
var app = builder.Build();
// Code removed for brevity.
Im vorherigen Beispiel ist die DomainRestrictedRequirement-Klasse sowohl ein IAuthorizationRequirement als auch ein eigener AuthorizationHandler für diese Anforderung. Es ist zulässig, diese beiden Komponenten in separate Klassen aufzuteilen, um die Verantwortlichkeiten zu trennen. Der Ansatz in diesem Beispiel bietet den Vorteil, dass das AuthorizationHandler beim Start nicht injiziert werden muss, da die Anforderung und der Handler ein und dasselbe sind.
Als Alternative zum Registrieren der DomainRestricted-Richtlinie und ihrem Verweis mit [Authorize("DomainRestricted")] können Sie ein IAuthorizationRequirementData-Attribut direkt auf einen Hub oder eine Hubmethode anwenden.
SignalR kombiniert die Anforderungen des Attributs in der effektiven Richtlinie für den Methodenaufruf. Weitere Informationen finden Sie unter Benutzerdefinierte Autorisierungsrichtlinien mit "IAuthorizationRequirementData".
Weitere Ressourcen
Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
Authentifizieren von Benutzern, die eine Verbindung mit einem SignalR-Hub herstellen
SignalR kann mit ASP.NET Core-Authentifizierung verwendet werden, um jeder Verbindung einen Benutzer zuzuordnen. In einem Hub kann über die HubConnectionContext.User-Eigenschaft auf Authentifizierungsdaten zugegriffen werden. Authentifizierung ermöglicht es dem Hub, Methoden für alle Verbindungen aufzurufen, die einem Benutzer zugeordnet sind. Weitere Informationen finden Sie unter Verwalten von Benutzern und Gruppen in SignalR. Einem einzelnen Benutzer können mehrere Verbindungen zugeordnet werden.
Nachfolgend finden Sie ein Beispiel für Startup.Configure, das SignalR- und ASP.NET Core-Authentifizierung verwendet:
public void Configure(IApplicationBuilder app)
{
...
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chat");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
Note
Wenn ein Token während der Lebensdauer einer Verbindung abläuft, funktioniert die Verbindung weiterhin.
LongPolling- und ServerSentEvents-Verbindungen schlagen bei nachfolgenden Anforderungen fehl, wenn sie keine neuen Zugriffstoken senden.
Cookie-Authentifizierung
In einer browserbasierten App ermöglicht cookie-Authentifizierung, dass Ihre vorhandenen Benutzeranmeldeinformationen automatisch in SignalR-Verbindungen einfließen. Bei Verwendung des Browserclients ist keine zusätzliche Konfiguration erforderlich. Wenn der Benutzer bei Ihrere App angemeldet ist, erbt die SignalR-Verbindung automatisch diese Authentifizierung.
Cookies sind eine browserspezifische Methode zum Senden von Zugriffstoken, aber Nicht-Browserclients können sie senden. Bei Verwendung des .NET-Clients kann die Cookies-Eigenschaft im .WithUrl-Aufruf so konfiguriert werden, dass ein cookie bereitgestellt wird. Die Verwendung von cookie-Authentifizierung über den .NET-Client erfordert jedoch, dass die App eine API zum Austauschen von Authentifizierungsdaten für ein cookie bereitstellt.
Bearer Tokenauthentifizierung
Der Client kann ein Zugriffstoken bereitstellen, anstatt ein cookie zu verwenden. Der Server überprüft das Token und verwendet es zum Identifizieren des Benutzers. Diese Überprüfung erfolgt nur, wenn die Verbindung hergestellt wird. Während der Dauer der Verbindung führt der Server keine automatische erneute Überprüfung durch, um festzustellen, ob das Token widerrufen wurde.
Im JavaScript-Client kann das Token mithilfe der Option accessTokenFactory bereitgestellt werden.
// Connect, using the token we got.
this.connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat", { accessTokenFactory: () => this.loginToken })
.build();
Im .NET-Client gibt es eine ähnliche AccessTokenProvider-Eigenschaft, die zum Konfigurieren des Tokens verwendet werden kann:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(_myAccessToken);
})
.Build();
Note
Die von Ihnen bereitgestellte Zugriffstokenfunktion wird vor jeder HTTP-Anforderung aufgerufen, die von SignalR vorgenommen wird. Wenn Sie das Token erneuern müssen, um die Verbindung aktiv zu halten (da sie während der Verbindung möglicherweise abläuft), führen Sie dies innerhalb dieser Funktion aus, und geben Sie das aktualisierte Token zurück.
In Standardweb-APIs werden Bearertoken in einem HTTP-Header gesendet. SignalR kann diese Header jedoch nicht in Browsern festlegen, wenn gewisse Transporte verwendet werden. Bei Verwendung von WebSockets und Server-Sent Events wird das Token als Abfragezeichenfolgenparameter übertragen.
JWT Integrierte Authentifizierung
Auf dem Server wird die Bearertokenauthentifizierung mithilfe der JWT Bearer-Middleware konfiguriert:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(options =>
{
// Identity made Cookie authentication the default.
// However, we want JWT Bearer Auth to be the default.
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// Configure the Authority to the expected value for your authentication provider
// This ensures the token is appropriately validated
options.Authority = /* TODO: Insert Authority URL here */;
// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or
// Server-Sent Events request comes in.
// Sending the access token in the query string is required when using WebSockets or ServerSentEvents
// due to a limitation in Browser APIs. We restrict it to only calls to the
// SignalR hub in this code.
// See https://docs.microsoft.com/aspnet/core/signalr/security#access-token-logging
// for more information about security considerations when using
// the query string to transmit the access token.
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSignalR();
// Change to use Name as the user identifier for SignalR
// WARNING: This requires that the source of your JWT token
// ensures that the Name claim is unique!
// If the Name claim isn't unique, users could receive messages
// intended for a different user!
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
// Change to use email as the user identifier for SignalR
// services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();
// WARNING: use *either* the NameUserIdProvider *or* the
// EmailBasedUserIdProvider, but do not use both.
}
Note
Die Abfragezeichenfolge wird in Browsern verwendet, wenn eine Verbindung mit WebSockets und Server-Sent Events aufgrund von Browser-API-Einschränkungen hergestellt wird. Bei Verwendung von HTTPS werden Abfragezeichenfolgenwerte durch die TLS-Verbindung gesichert. Viele Server protokollieren jedoch Abfragezeichenfolgenwerte. Weitere Informationen finden Sie unter Sicherheitsüberlegungen zu ASP.NET CoreSignalR. SignalR verwendet Header, um Token in Umgebungen zu übertragen, die diese unterstützen (z. B. die .NET- und Java-Clients).
IdentityServerauthentifizierung JWT
Wenn Sie Identity-Server verwenden, fügen Sie dem Projekt einen PostConfigureOptions<TOptions>-Dienst hinzu:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{
public void PostConfigure(string name, JwtBearerOptions options)
{
var originalOnMessageReceived = options.Events.OnMessageReceived;
options.Events.OnMessageReceived = async context =>
{
await originalOnMessageReceived(context);
if (string.IsNullOrEmpty(context.Token))
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
}
};
}
}
Registrieren Sie den Dienst in Startup.ConfigureServices nach dem Hinzufügen von Diensten für die Authentifizierung (AddAuthentication) und dem Authentifizierungshandler für Identity-Server (AddIdentityServerJwt):
services.AddAuthentication()
.AddIdentityServerJwt();
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,
ConfigureJwtBearerOptions>());
Cookies vs. Bearertoken
Cookies sind spezifisch für Browser. Das Senden von Authentifizierungs-Token über andere Arten von Clients erhöht die Komplexität im Vergleich zum Versenden von Bearer-Token. Daher wird die cookie-Authentifizierung nicht empfohlen, es sei denn, die App muss Benutzer ausschließlich vom Browser-Client aus authentifizieren. Bearer Die Tokenauthentifizierung ist der empfohlene Ansatz bei der Verwendung anderer Clients als des Browserclients.
Windows-Authentifizierung
Wenn Windows-Authentifizierung in Ihrer App konfiguriert ist, können SignalR diese Identität verwenden, um Hubs zu schützen. Um jedoch Nachrichten an einzelne Benutzer zu senden, müssen Sie einen benutzerdefinierten Benutzer-ID-Anbieter hinzufügen. Das Windows-Authentifizierungssystem stellt den Anspruch „Namenbezeichner“ nicht bereit. SignalR verwendet den Anspruch, um den Benutzernamen zu bestimmen.
Fügen Sie eine neue Klasse hinzu, die IUserIdProvider implementiert, und rufen Sie einen der Ansprüche vom Benutzer ab, um ihn als Bezeichner zu verwenden. Um beispielsweise den Anspruch „Name“ zu verwenden (der Windows-Benutzername im Format [Domain]\[Username]), erstellen Sie die folgende Klasse:
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
Anstelle von ClaimTypes.Name können Sie einen beliebigen Wert aus User verwenden (z. B. den Windows-SID-Bezeichner usw.).
Note
Der von Ihnen ausgewählte Wert muss für alle Benutzer in Ihrem System eindeutig sein. Andernfalls kann eine Nachricht, die für einen Benutzer bestimmt ist, ggf. an einen anderen Benutzer gesendet werden.
Registrieren Sie diese Komponente in Ihrer Startup.ConfigureServices-Methode.
public void ConfigureServices(IServiceCollection services)
{
// ... other services ...
services.AddSignalR();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
}
Im .NET-Client muss Windows-Authentifizierung durch Festlegen der UseDefaultCredentials-Eigenschaft aktiviert werden:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.UseDefaultCredentials = true;
})
.Build();
Windows-Authentifizierung wird in Internet Explorer und Microsoft Edge unterstützt, aber nicht in allen Browsern. In Chrome und Safari schlägt der Versuch beispielsweise fehl, Windows-Authentifizierung und WebSockets zu verwenden. Wenn Windows-Authentifizierung fehlschlägt, versucht der Client, auf andere Transporte zurückzugreifen, die möglicherweise funktionieren.
Ansprüche nutzen, um die Identitätsverwaltung anzupassen
Eine App, die Benutzer authentifiziert, kann SignalR-Benutzer-IDs von Benutzeransprüchen ableiten. Implementieren Sie SignalR, und registrieren Sie die Implementierung, um anzugeben, wie IUserIdProvider Benutzer-IDs erstellt.
Der Beispielcode zeigt, wie Sie mithilfe von Ansprüchen die E-Mail-Adresse des Benutzers als identifizierende Eigenschaft auswählen können.
Note
Der von Ihnen ausgewählte Wert muss für alle Benutzer in Ihrem System eindeutig sein. Andernfalls kann eine Nachricht, die für einen Benutzer bestimmt ist, ggf. an einen anderen Benutzer gesendet werden.
public class EmailBasedUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
Die Kontoregistrierung fügt der ASP.NET Identitätsdatenbank einen Anspruch mit typ ClaimsTypes.Email hinzu.
// create a new user
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user, Input.Password);
// add the email claim and value for this user
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Email, Input.Email));
Registrieren Sie bitte diese Komponente in Ihrem Startup.ConfigureServices.
services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();
Autorisieren von Benutzern für den Zugriff auf Hubs und Hubmethoden
Standardmäßig können alle Methoden in einem Hub von einem nicht authentifizierten Benutzer aufgerufen werden. Wenden Sie das AuthorizeAttribute-Attribut auf den Hub an, um Authentifizierung zu verlangen:
[Authorize]
public class ChatHub: Hub
{
}
Sie können die Konstruktorargumente und -eigenschaften des [Authorize]-Attributs verwenden, um den Zugriff auf Benutzer zu beschränken, die bestimmten Autorisierungsrichtlinien entsprechen. Wenn Sie z. B. eine benutzerdefinierte Autorisierungsrichtlinie namens MyAuthorizationPolicy besitzen, können Sie sicherstellen, dass nur Benutzer, die dieser Richtlinie entsprechen, mit dem folgenden Code auf den Hub zugreifen können:
[Authorize("MyAuthorizationPolicy")]
public class ChatHub : Hub
{
}
Auf einzelne Hubmethoden kann auch das Attribut [Authorize] angewendet werden. Wenn der aktuelle Benutzer nicht der für die Methode geltenden Richtlinie entspricht, wird ein Fehler an den Aufrufer zurückgegeben:
[Authorize]
public class ChatHub : Hub
{
public async Task Send(string message)
{
// ... send a message to all users ...
}
[Authorize("Administrators")]
public void BanUser(string userName)
{
// ... ban a user from the chat room (something only Administrators can do) ...
}
}
Verwenden von Autorisierungshandlern zum Anpassen der Hubmethodenautorisierung
SignalR bietet eine benutzerdefinierte Ressource für Autorisierungshandler, wenn eine Hubmethode Autorisierung erfordert. Die Ressource ist eine Instanz von HubInvocationContext.
HubInvocationContext enthält HubCallerContext, den Namen der aufgerufenen Hubmethode, sowie die Argumente für die Hubmethode.
Sehen Sie sich das Beispiel für einen Chatroom an, der mehreren Mitgliedern einer Organisation die Anmeldung über Microsoft Entra ID ermöglicht. Jede Person mit einem Microsoft-Konto kann sich beim Chat anmelden, aber nur Mitglieder der besitzenden Organisation sollte in der Lage sein, Benutzer zu sperren oder den Chatverlauf anderer Benutzer anzuzeigen. Darüber hinaus können Sie bestimmte Funktionen für bestimmte Benutzer einschränken. Mit den aktualisierten Features in ASP.NET Core 3.0 ist dies durchaus möglich. Beachten Sie, dass DomainRestrictedRequirement als benutzerdefinierte IAuthorizationRequirement dient. Nachdem der HubInvocationContext-Ressourcenparameter übergeben wurde, kann die interne Logik den Kontext untersuchen, in dem der Hub aufgerufen wird, und Entscheidungen treffen, ob der Benutzer einzelne Hubmethoden ausführen darf.
[Authorize]
public class ChatHub : Hub
{
public void SendMessage(string message)
{
}
[Authorize("DomainRestricted")]
public void BanUser(string username)
{
}
[Authorize("DomainRestricted")]
public void ViewUserHistory(string username)
{
}
}
public class DomainRestrictedRequirement :
AuthorizationHandler<DomainRestrictedRequirement, HubInvocationContext>,
IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
DomainRestrictedRequirement requirement,
HubInvocationContext resource)
{
if (IsUserAllowedToDoThis(resource.HubMethodName, context.User.Identity.Name) &&
context.User.Identity.Name.EndsWith("@microsoft.com"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
private bool IsUserAllowedToDoThis(string hubMethodName,
string currentUsername)
{
return !(currentUsername.Equals("asdf42@microsoft.com") &&
hubMethodName.Equals("banUser", StringComparison.OrdinalIgnoreCase));
}
}
Fügen Sie in Startup.ConfigureServices die neue Richtlinie hinzu, und geben Sie dabei die benutzerdefinierte DomainRestrictedRequirement-Anforderung als Parameter an, um die DomainRestricted-Richtlinie zu erstellen.
public void ConfigureServices(IServiceCollection services)
{
// ... other services ...
services
.AddAuthorization(options =>
{
options.AddPolicy("DomainRestricted", policy =>
{
policy.Requirements.Add(new DomainRestrictedRequirement());
});
});
}
Im vorherigen Beispiel ist die DomainRestrictedRequirement-Klasse sowohl ein IAuthorizationRequirement als auch ein eigener AuthorizationHandler für diese Anforderung. Es ist zulässig, diese beiden Komponenten in separate Klassen aufzuteilen, um die Verantwortlichkeiten zu trennen. Ein Vorteil des Ansatzes des Beispiels besteht darin, dass AuthorizationHandler beim Start nicht injiziert werden müssen, weil die Anforderung und der Handler ein und dasselbe sind.