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();

Dekont

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.

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.

Cookies, erişim belirteçlerini göndermek için tarayıcıya özgü bir yoldur, 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();

Dekont

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();

Dekont

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.

Cookies ve taşıyıcı belirteçleri karşılaştırması

Cookietarayı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 bu kimliği kullanabilir. 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 Userkullanın.

Dekont

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.cskaydedin:

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.

Kimlik işlemeyi özelleştirmek 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.

Dekont

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 kimlik 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.cskaydedin:

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 (nasıl indirilir)

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?}");
    });
}

Dekont

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.

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.

Cookies, erişim belirteçlerini göndermek için tarayıcıya özgü bir yoldur, 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();

Dekont

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.

Dekont

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>());

Cookies ve taşıyıcı belirteçleri karşılaştırması

Cookietarayı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 bu kimliği kullanabilir. 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.

Dekont

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.

Kimlik işlemeyi özelleştirmek 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.

Dekont

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 kimlik 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.ConfigureServiceskaydedin.

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 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)
    {
    }
}

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