ASP.NET Core'da kimlik doğrulaması ve yetkilendirme SignalR
Hub'a SignalR bağlanan kullanıcıların kimliğini doğrulama
SignalRbir kullanıcıyı her bağlantıyla ilişkilendirmek için ASP.NET Core kimlik doğrulaması ile kullanılabilir. Bir hub'da kimlik doğrulama verilerine özelliğinden HubConnectionContext.User erişilebilir. Kimlik doğrulaması, hub'ın bir kullanıcıyla ilişkili tüm bağlantılarda yöntemleri çağırmasına olanak tanır. Daha fazla bilgi için bkz . içindeki SignalRkullanıcıları ve grupları yönetme. Tek bir kullanıcıyla birden çok bağlantı ilişkilendirilebilir.
Aşağıdaki kod, Core kimlik doğrulamasını kullanan SignalR ve ASP.NET bir örnektir:
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();
Not
Bir belirtecin süresi bağlantının ömrü boyunca dolarsa, varsayılan olarak bağlantı çalışmaya devam eder. LongPolling
ve ServerSentEvent
bağlantılar yeni erişim belirteçleri göndermezse sonraki isteklerde başarısız olur. Kimlik doğrulama belirtecinin süresi dolduğunda bağlantıların kapatılması için CloseOnAuthenticationExpiration değerini ayarlayın.
Cookie kimlik doğrulaması
Tarayıcı tabanlı bir uygulamada kimlik doğrulaması, cookie mevcut kullanıcı kimlik bilgilerinin otomatik olarak bağlantılara akmasına SignalR olanak tanır. Tarayıcı istemcisini kullanırken ek yapılandırma gerekmez. Kullanıcı bir uygulamada oturum açtıysa, bağlantı otomatik olarak bu kimlik doğrulamasını SignalR devralır.
Tanımlama bilgileri, erişim belirteçleri göndermenin tarayıcıya özgü bir yoludur, ancak tarayıcı olmayan istemciler bunları gönderebilir. .NET İstemcisi kullanılırken, Cookies
özelliği çağrısında .WithUrl
bir cookiesağlamak üzere yapılandırılabilir. Ancak, .NET istemcisinden kimlik doğrulaması kullanmak cookie için uygulamanın için kimlik doğrulama verilerini değiştirmek üzere cookiebir API sağlaması gerekir.
Taşıyıcı belirteç kimlik doğrulaması
İstemci, kullanmak cookieyerine bir erişim belirteci sağlayabilir. Sunucu belirteci doğrular ve kullanıcıyı tanımlamak için kullanır. Bu doğrulama yalnızca bağlantı kurulduğunda yapılır. Bağlantının ömrü boyunca, sunucu belirteç iptalini denetlemek için otomatik olarak yeniden doğrulamaz.
JavaScript istemcisinde, belirteç accessTokenFactory seçeneği kullanılarak sağlanabilir.
// Connect, using the token we got.
this.connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat", { accessTokenFactory: () => this.loginToken })
.build();
.NET istemcisinde, belirteci yapılandırmak için kullanılabilecek benzer bir AccessTokenProvider özelliği vardır:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(_myAccessToken);
})
.Build();
Not
Sağlanan erişim belirteci işlevi, tarafından SignalRyapılan her HTTP isteğinden önce çağrılır. Bağlantıyı etkin tutmak için belirtecin yenilenmesi gerekiyorsa, bunu bu işlevin içinden yapın ve güncelleştirilmiş belirteci döndürin. Bağlantı sırasında süresinin dolmaması için belirtecin yenilenmesi gerekebilir.
Standart web API'lerinde taşıyıcı belirteçler bir HTTP üst bilgisinde gönderilir. Ancak, SignalR bazı aktarımlar kullanılırken tarayıcılarda bu üst bilgileri ayarlayamıyor. WebSockets ve Sunucu Tarafından Gönderilen Olaylar kullanılırken, belirteç sorgu dizesi parametresi olarak iletilir.
Yerleşik JWT kimlik doğrulaması
Sunucuda taşıyıcı belirteç kimlik doğrulaması, JWT Taşıyıcı ara yazılımı kullanılarak yapılandırılır:
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();
Not
Sorgu dizesi, tarayıcı API'sinin sınırlamaları nedeniyle WebSockets ve Sunucu Tarafından Gönderilen Olaylar ile bağlanırken tarayıcılarda kullanılır. HTTPS kullanırken, sorgu dizesi değerleri TLS bağlantısı tarafından güvenli hale getirilir. Ancak, birçok sunucu sorgu dizesi değerlerini günlüğe kaydeder. Daha fazla bilgi için bkz . ASP.NET Core'da SignalRgüvenlikle ilgili dikkat edilmesi gerekenler. SignalR , bunları destekleyen ortamlarda (.NET ve Java istemcileri gibi) belirteçleri iletmek için üst bilgileri kullanır.
Identity Sunucu JWT kimlik doğrulaması
Duende IdentityServer kullanırken projeye bir PostConfigureOptions<TOptions> hizmet ekleyin:
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;
}
}
};
}
}
Kimlik doğrulaması () için hizmetleri ve SunucuAddIdentityServerJwt (AddAuthentication) için Identity kimlik doğrulama işleyicisini ekledikten sonra hizmeti kaydedin:
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.
Tanımlama bilgileri ile taşıyıcı belirteçleri karşılaştırması
Tanımlama bilgileri tarayıcılara özeldir. Bunları diğer istemci türlerinden göndermek, taşıyıcı belirteçleri göndermeye kıyasla karmaşıklık katıyor. Cookie uygulamanın yalnızca tarayıcı istemcisinden kullanıcıların kimliğini doğrulaması gerekmediği sürece kimlik doğrulaması önerilmez. Taşıyıcı belirteci kimlik doğrulaması, tarayıcı istemcisi dışındaki istemcileri kullanırken önerilen yaklaşımdır.
Windows kimlik doğrulması
Uygulamada Windows kimlik doğrulaması yapılandırıldıysa, SignalR hub'ların güvenliğini sağlamak için bunu identity kullanabilirsiniz. Ancak, tek tek kullanıcılara ileti göndermek için özel bir Kullanıcı Kimliği sağlayıcısı ekleyin. Windows kimlik doğrulama sistemi "Ad Tanımlayıcısı" talebi sağlamaz. SignalR kullanıcı adını belirlemek için talebi kullanır.
Tanımlayıcı olarak kullanmak üzere kullanıcıdan taleplerden birini uygulayan IUserIdProvider
ve alan yeni bir sınıf ekleyin. Örneğin, "Ad" talebi (formdaki [Domain]/[Username]
Windows kullanıcı adıdır) kullanmak için aşağıdaki sınıfı oluşturun:
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
yerine ClaimTypes.Name
, windows SID tanımlayıcısı gibi herhangi bir değeri User
kullanın.
Not
Seçilen değer sistemdeki tüm kullanıcılar arasında benzersiz olmalıdır. Aksi takdirde, bir kullanıcıya yönelik bir ileti farklı bir kullanıcıya gidebilir.
Bu bileşeni içinde Program.cs
kaydedin:
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.
.NET İstemcisi'nde, özelliği ayarlanarak UseDefaultCredentials Windows Kimlik Doğrulaması etkinleştirilmelidir:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.UseDefaultCredentials = true;
})
.Build();
Windows kimlik doğrulaması Microsoft Edge'de desteklenir, ancak tüm tarayıcılarda desteklenmez. Örneğin, Chrome ve Safari'de Windows kimlik doğrulamasını kullanmaya çalışmak ve WebSockets başarısız olur. Windows kimlik doğrulaması başarısız olduğunda istemci, işe yarayabilecek diğer aktarımlara geri dönmeye çalışır.
İşlemeyi özelleştirmek identity için talepleri kullanma
Kullanıcıların kimliğini doğrulayan bir uygulama, kullanıcı taleplerinden kullanıcı kimlikleri türetebilir SignalR . Kullanıcı kimliklerinin nasıl SignalR oluşturacağını belirtmek için uygulamayı uygulayın IUserIdProvider
ve kaydedin.
Örnek kod, tanımlayıcı özellik olarak kullanıcının e-posta adresini seçmek için taleplerin nasıl kullanılacağını gösterir.
Not
Seçilen değer sistemdeki tüm kullanıcılar arasında benzersiz olmalıdır. Aksi takdirde, bir kullanıcıya yönelik bir ileti farklı bir kullanıcıya gidebilir.
public class EmailBasedUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value!;
}
}
Hesap kaydı, ASP.NET identity veritabanına türe ClaimsTypes.Email
sahip bir talep ekler.
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.
Bu bileşeni içinde Program.cs
kaydedin:
builder.Services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();
Kullanıcıları hub'lara ve hub yöntemlerine erişme yetkisi verme
Varsayılan olarak, bir hub'daki tüm yöntemler kimliği doğrulanmamış bir kullanıcı tarafından çağrılabilir. Kimlik doğrulaması istemek için özniteliğini hub'a uygulayın AuthorizeAttribute :
[Authorize]
public class ChatHub: Hub
{
}
Özniteliğin [Authorize]
oluşturucu bağımsız değişkenleri ve özellikleri, erişimi yalnızca belirli yetkilendirme ilkeleriyle eşleşen kullanıcılarla kısıtlamak için kullanılabilir. Örneğin, adlı MyAuthorizationPolicy
özel yetkilendirme ilkesiyle, yalnızca bu ilkeyle eşleşen kullanıcılar aşağıdaki kodu kullanarak hub'a erişebilir:
[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.
[Authorize]
Özniteliği tek tek hub yöntemlerine uygulanabilir. Geçerli kullanıcı yöntemine uygulanan ilkeyle eşleşmiyorsa, arayana bir hata döndürülür:
[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) ...
}
}
Hub yöntemi yetkilendirmesini özelleştirmek için yetkilendirme işleyicilerini kullanma
SignalR bir hub yöntemi yetkilendirme gerektirdiğinde yetkilendirme işleyicilerine özel bir kaynak sağlar. Kaynak bir örneğidir HubInvocationContext. , HubInvocationContext
çağrılan hub yönteminin adını ve hub yönteminin bağımsız değişkenlerini içerir HubCallerContext.
Microsoft Entra Id aracılığıyla birden çok kuruluşta oturum açmaya izin veren bir sohbet odası örneğini düşünün. Microsoft hesabı olan herkes sohbette oturum açabilir, ancak yalnızca sahibi olan kuruluşun üyeleri kullanıcıları yasaklayabilir veya kullanıcıların sohbet geçmişlerini görüntüleyebilir. Ayrıca belirli kullanıcıların bazı işlevlerini kısıtlamak isteyebiliriz. öğesinin DomainRestrictedRequirement
özel IAuthorizationRequirementolarak nasıl hizmet verdiklerine dikkat edin. Artık kaynak parametresi geçirildiğine HubInvocationContext
göre, iç mantık Hub'ın çağrıldığı bağlamı inceleyebilir ve kullanıcının tek tek Hub yöntemlerini yürütmesine izin verme konusunda kararlar alabilir:
[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));
}
}
içinde Program.cs
, ilkeyi oluşturmak için özel DomainRestrictedRequirement
gereksinimi parametre olarak sağlayarak yeni ilkeyi DomainRestricted
ekleyin:
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.
Yukarıdaki örnekte sınıfı, DomainRestrictedRequirement
bu gereksinim için hem bir IAuthorizationRequirement
hem de kendi AuthorizationHandler
sınıfıdır. Endişeleri ayırmak için bu iki bileşeni ayrı sınıflara bölmek kabul edilebilir. Örneğin yaklaşımının bir avantajı, gereksinim ve işleyici aynı şey olduğundan başlangıç sırasında eklemeye AuthorizationHandler
gerek olmamasıdır.
Ek kaynaklar
Örnek kodu görüntüleme veya indirme (indirme)
Hub'a SignalR bağlanan kullanıcıların kimliğini doğrulama
SignalRbir kullanıcıyı her bağlantıyla ilişkilendirmek için ASP.NET Core kimlik doğrulaması ile kullanılabilir. Bir hub'da kimlik doğrulama verilerine özelliğinden HubConnectionContext.User erişilebilir. Kimlik doğrulaması, hub'ın bir kullanıcıyla ilişkili tüm bağlantılarda yöntemleri çağırmasına olanak tanır. Daha fazla bilgi için bkz . içindeki SignalRkullanıcıları ve grupları yönetme. Tek bir kullanıcıyla birden çok bağlantı ilişkilendirilebilir.
Aşağıda, ve ASP.NET Core kimlik doğrulamasını Startup.Configure
kullanan SignalR bir örnek verilmiştir:
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?}");
});
}
Not
Bağlantının ömrü boyunca bir belirtecin süresi dolarsa bağlantı çalışmaya devam eder. LongPolling
ve ServerSentEvent
bağlantılar yeni erişim belirteçleri göndermezse sonraki isteklerde başarısız olur.
Cookie kimlik doğrulaması
Tarayıcı tabanlı bir uygulamada kimlik doğrulaması, cookie mevcut kullanıcı kimlik bilgilerinizin otomatik olarak bağlantılara akmasını SignalR sağlar. Tarayıcı istemcisini kullanırken ek yapılandırma gerekmez. Kullanıcı uygulamanızda oturum açtıysa, bağlantı otomatik olarak bu kimlik doğrulamasını SignalR devralır.
Tanımlama bilgileri, erişim belirteçleri göndermenin tarayıcıya özgü bir yoludur, ancak tarayıcı olmayan istemciler bunları gönderebilir. .NET İstemcisi kullanılırken, Cookies
özelliği çağrısında .WithUrl
bir cookiesağlamak üzere yapılandırılabilir. Ancak, .NET istemcisinden kimlik doğrulaması kullanmak cookie için uygulamanın için kimlik doğrulama verilerini değiştirmek üzere cookiebir API sağlaması gerekir.
Taşıyıcı belirteç kimlik doğrulaması
İstemci, kullanmak cookieyerine bir erişim belirteci sağlayabilir. Sunucu belirteci doğrular ve kullanıcıyı tanımlamak için kullanır. Bu doğrulama yalnızca bağlantı kurulduğunda yapılır. Bağlantının ömrü boyunca, sunucu belirteç iptalini denetlemek için otomatik olarak yeniden doğrulamaz.
JavaScript istemcisinde, belirteç accessTokenFactory seçeneği kullanılarak sağlanabilir.
// Connect, using the token we got.
this.connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat", { accessTokenFactory: () => this.loginToken })
.build();
.NET istemcisinde, belirteci yapılandırmak için kullanılabilecek benzer bir AccessTokenProvider özelliği vardır:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(_myAccessToken);
})
.Build();
Not
Sağladığınız erişim belirteci işlevi, tarafından SignalRyapılan her HTTP isteğinden önce çağrılır. Bağlantıyı etkin tutmak için belirteci yenilemeniz gerekiyorsa (bağlantı sırasında süresi dolabileceği için), bunu bu işlevin içinden yapın ve güncelleştirilmiş belirteci döndürin.
Standart web API'lerinde taşıyıcı belirteçler bir HTTP üst bilgisinde gönderilir. Ancak, SignalR bazı aktarımlar kullanılırken tarayıcılarda bu üst bilgileri ayarlayamıyor. WebSockets ve Sunucu Tarafından Gönderilen Olaylar kullanılırken, belirteç sorgu dizesi parametresi olarak iletilir.
Yerleşik JWT kimlik doğrulaması
Sunucuda taşıyıcı belirteç kimlik doğrulaması, JWT Taşıyıcı ara yazılımı kullanılarak yapılandırılır:
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.
}
Kod açıklamalarının İngilizce dışındaki dillere çevirisini görmek isterseniz, bunu bu GitHub tartışma konusunda bize bildirin.
Not
Sorgu dizesi, tarayıcı API'sinin sınırlamaları nedeniyle WebSockets ve Sunucu Tarafından Gönderilen Olaylar ile bağlanırken tarayıcılarda kullanılır. HTTPS kullanırken, sorgu dizesi değerleri TLS bağlantısı tarafından güvenli hale getirilir. Ancak, birçok sunucu sorgu dizesi değerlerini günlüğe kaydeder. Daha fazla bilgi için bkz . ASP.NET Core'da SignalRgüvenlikle ilgili dikkat edilmesi gerekenler. SignalR , bunları destekleyen ortamlarda (.NET ve Java istemcileri gibi) belirteçleri iletmek için üst bilgileri kullanır.
Identity Sunucu JWT kimlik doğrulaması
Sunucu kullanırken Identity projeye bir PostConfigureOptions<TOptions> hizmet ekleyin:
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;
}
}
};
}
}
Kimlik doğrulaması () için hizmetleri ve SunucuAddIdentityServerJwt (AddAuthentication) için Identity kimlik doğrulama işleyicisini ekledikten sonra hizmeti 'ye Startup.ConfigureServices
kaydedin:
services.AddAuthentication()
.AddIdentityServerJwt();
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,
ConfigureJwtBearerOptions>());
Tanımlama bilgileri ile taşıyıcı belirteçleri karşılaştırması
Tanımlama bilgileri tarayıcılara özeldir. Bunları diğer istemci türlerinden göndermek, taşıyıcı belirteçleri göndermeye kıyasla karmaşıklık katıyor. Sonuç olarak, cookie uygulamanın yalnızca tarayıcı istemcisinden kullanıcıların kimliğini doğrulaması gerekmediği sürece kimlik doğrulaması önerilmez. Taşıyıcı belirteci kimlik doğrulaması, tarayıcı istemcisi dışındaki istemcileri kullanırken önerilen yaklaşımdır.
Windows kimlik doğrulması
Uygulamanızda Windows kimlik doğrulaması yapılandırıldıysa, SignalR hub'ların güvenliğini sağlamak için bunu identity kullanabilirsiniz. Ancak, tek tek kullanıcılara ileti göndermek için özel bir Kullanıcı Kimliği sağlayıcısı eklemeniz gerekir. Windows kimlik doğrulama sistemi "Ad Tanımlayıcısı" talebi sağlamaz. SignalR kullanıcı adını belirlemek için talebi kullanır.
Tanımlayıcı olarak kullanmak üzere kullanıcıdan taleplerden birini uygulayan IUserIdProvider
ve alan yeni bir sınıf ekleyin. Örneğin, "Ad" talebi (formdaki [Domain]\[Username]
Windows kullanıcı adıdır) kullanmak için aşağıdaki sınıfı oluşturun:
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
yerine ClaimTypes.Name
, üzerindeki User
herhangi bir değeri (Windows SID tanımlayıcısı gibi) kullanabilirsiniz.
Not
Seçtiğiniz değer sisteminizdeki tüm kullanıcılar arasında benzersiz olmalıdır. Aksi takdirde, bir kullanıcıya yönelik bir ileti farklı bir kullanıcıya gidebilir.
Bu bileşeni yönteminize Startup.ConfigureServices
kaydedin.
public void ConfigureServices(IServiceCollection services)
{
// ... other services ...
services.AddSignalR();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
}
.NET İstemcisi'nde, özelliği ayarlanarak UseDefaultCredentials Windows Kimlik Doğrulaması etkinleştirilmelidir:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.UseDefaultCredentials = true;
})
.Build();
Windows kimlik doğrulaması Internet Explorer ve Microsoft Edge'de desteklenir, ancak tüm tarayıcılarda desteklenmez. Örneğin, Chrome ve Safari'de Windows kimlik doğrulamasını kullanmaya çalışmak ve WebSockets başarısız olur. Windows kimlik doğrulaması başarısız olduğunda istemci, işe yarayabilecek diğer aktarımlara geri dönmeye çalışır.
İşlemeyi özelleştirmek identity için talepleri kullanma
Kullanıcıların kimliğini doğrulayan bir uygulama, kullanıcı taleplerinden kullanıcı kimlikleri türetebilir SignalR . Kullanıcı kimliklerinin nasıl SignalR oluşturacağını belirtmek için uygulamayı uygulayın IUserIdProvider
ve kaydedin.
Örnek kod, tanımlayıcı özellik olarak kullanıcının e-posta adresini seçmek için talepleri nasıl kullanacağınızı gösterir.
Not
Seçtiğiniz değer sisteminizdeki tüm kullanıcılar arasında benzersiz olmalıdır. Aksi takdirde, bir kullanıcıya yönelik bir ileti farklı bir kullanıcıya gidebilir.
public class EmailBasedUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
Hesap kaydı, ASP.NET identity veritabanına türe ClaimsTypes.Email
sahip bir talep ekler.
// 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));
Bu bileşeni'nize Startup.ConfigureServices
kaydedin.
services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();
Kullanıcıları hub'lara ve hub yöntemlerine erişme yetkisi verme
Varsayılan olarak, bir hub'daki tüm yöntemler kimliği doğrulanmamış bir kullanıcı tarafından çağrılabilir. Kimlik doğrulaması istemek için özniteliğini hub'a uygulayın AuthorizeAttribute :
[Authorize]
public class ChatHub: Hub
{
}
Yalnızca belirli yetkilendirme ilkeleriyle eşleşen kullanıcılara erişimi kısıtlamak için özniteliğin [Authorize]
oluşturucu bağımsız değişkenlerini ve özelliklerini kullanabilirsiniz. Örneğin, adlı MyAuthorizationPolicy
özel bir yetkilendirme ilkeniz varsa, yalnızca bu ilkeyle eşleşen kullanıcıların aşağıdaki kodu kullanarak hub'a erişebildiğinden emin olabilirsiniz:
[Authorize("MyAuthorizationPolicy")]
public class ChatHub : Hub
{
}
Tek tek hub yöntemlerinde [Authorize]
de özniteliği uygulanabilir. Geçerli kullanıcı yöntemine uygulanan ilkeyle eşleşmiyorsa, arayana bir hata döndürülür:
[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) ...
}
}
Hub yöntemi yetkilendirmesini özelleştirmek için yetkilendirme işleyicilerini kullanma
SignalR bir hub yöntemi yetkilendirme gerektirdiğinde yetkilendirme işleyicilerine özel bir kaynak sağlar. Kaynak bir örneğidir HubInvocationContext
. , HubInvocationContext
çağrılan hub yönteminin adını ve hub yönteminin bağımsız değişkenlerini içerir HubCallerContext
.
Microsoft Entra Id aracılığıyla birden çok kuruluşta oturum açmaya izin veren bir sohbet odası örneğini düşünün. Microsoft hesabı olan herkes sohbette oturum açabilir, ancak yalnızca sahibi olan kuruluşun üyeleri kullanıcıları yasaklayabilir veya kullanıcıların sohbet geçmişlerini görüntüleyebilir. Ayrıca, belirli kullanıcıların belirli işlevlerini kısıtlamak isteyebiliriz. ASP.NET Core 3.0'daki güncelleştirilmiş özellikler kullanıldığında bu tamamen mümkündür. öğesinin DomainRestrictedRequirement
özel IAuthorizationRequirement
olarak nasıl hizmet verdiklerine dikkat edin. Artık kaynak parametresi geçirildiğine HubInvocationContext
göre, iç mantık Hub'ın çağrıldığı bağlamı inceleyebilir ve kullanıcının tek tek Hub yöntemlerini yürütmesine izin verme konusunda kararlar alabilir.
[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));
}
}
içinde Startup.ConfigureServices
, ilkeyi oluşturmak için özel DomainRestrictedRequirement
gereksinimi parametre olarak sağlayarak yeni ilkeyi DomainRestricted
ekleyin.
public void ConfigureServices(IServiceCollection services)
{
// ... other services ...
services
.AddAuthorization(options =>
{
options.AddPolicy("DomainRestricted", policy =>
{
policy.Requirements.Add(new DomainRestrictedRequirement());
});
});
}
Yukarıdaki örnekte sınıfı, DomainRestrictedRequirement
bu gereksinim için hem bir IAuthorizationRequirement
hem de kendi AuthorizationHandler
sınıfıdır. Endişeleri ayırmak için bu iki bileşeni ayrı sınıflara bölmek kabul edilebilir. Örneğin yaklaşımının bir avantajı, gereksinim ve işleyici aynı şey olduğundan başlangıç sırasında eklemeye AuthorizationHandler
gerek olmamasıdır.
Ek kaynaklar
ASP.NET Core