ASP.NET Core kimlik doğrulaması ve yetkilendirmeSignalR
Örnek kodu görüntüleme veya indirme (nasıl indirileceği)
Bir hub 'a bağlanan kullanıcıların kimliğini doğrulama SignalR
SignalR, bir kullanıcıyı her bağlantıyla ilişkilendirmek için ASP.NET Core kimlik doğrulamasıyla birlikte kullanılabilir. Bir hub 'da, Hubconnectioncontext. User özelliğinden kimlik doğrulama verilerine erişilebilir. Kimlik doğrulaması, hub 'ın bir kullanıcıyla ilişkili tüm bağlantılar üzerinde Yöntemler çağırmasını sağlar. Daha fazla bilgi için bkz. kullanıcıları ve grupları ' SignalR de yönetme . Birden çok bağlantı tek bir kullanıcıyla ilişkilendirilebilir.
aşağıda, Startup.Configure ASP.NET Core kimlik doğrulaması kullanan bir örnek verilmiştir SignalR :
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?}");
});
}
public void Configure(IApplicationBuilder app)
{
...
app.UseStaticFiles();
app.UseAuthentication();
app.UseSignalR(hubs =>
{
hubs.MapHub<ChatHub>("/chat");
});
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
Not
SignalRve ASP.NET Core kimlik doğrulama ara yazılımını kaydetme sırası önemli. UseAuthentication UseSignalR SignalR Üzerinde bir kullanıcı olması için önce her zaman ' i çağırın HttpContext .
Not
Bir bağlantının kullanım ömrü boyunca belirtecin süresi dolarsa bağlantı çalışmaya devam eder. LongPolling ve ServerSentEvent Bağlantılar yeni erişim belirteçleri gönderiyorlarsa sonraki isteklerde başarısız olur.
Not
Bir bağlantının ömrü boyunca belirtecin süresi dolarsa, bağlantı varsayılan olarak çalışmaya devam eder. LongPolling ve ServerSentEvent Bağlantılar yeni erişim belirteçleri gönderiyorlarsa sonraki isteklerde başarısız olur. Kimlik doğrulama belirtecinin süresi dolduğunda kapatılacak bağlantıların kapatılması için Closeonauthenticationexpiration' ı ayarlayın.
Cookie yetkilendirmesi
Tarayıcı tabanlı bir uygulamada, cookie kimlik doğrulama mevcut kullanıcı kimlik bilgilerinizin otomatik olarak bağlantılara akmasını sağlar SignalR . Tarayıcı istemcisi kullanılırken ek yapılandırma gerekmez. Kullanıcı uygulamanızda oturum açtıysa, SignalR bağlantı bu kimlik doğrulamasını otomatik olarak devralır.
Cookie, erişim belirteçleri göndermek için tarayıcıya özgü bir yoldur, ancak tarayıcı olmayan istemciler bu uygulamaları gönderebilir. .NET istemcisikullanılırken, Cookies özelliği .WithUrl sağlamak için çağrısında yapılandırılabilir cookie . Ancak, cookie .net istemcisinden kimlik doğrulaması kullanmak, uygulamanın bir için kimlik doğrulama verilerini Exchange için BIR API sağlamasını gerektirir cookie .
Taşıyıcı belirteç kimlik doğrulaması
İstemci, kullanmak yerine bir erişim belirteci sağlayabilir cookie . Sunucu belirteci doğrular ve kullanıcıyı tanımlamak için kullanır. Bu doğrulama yalnızca bağlantı kurulduunda yapılır. Bağlantının kullanım ömrü boyunca, sunucu otomatik olarak yeniden doğrulamadan belirteç iptalini kontrol etmez.
JavaScript istemcisinde, belirteç Accesstokenfactory seçeneği kullanılarak sağlanabilirler.
// 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 yapılan her http isteğinin önüne çağırılır SignalR . Bağlantıyı etkin tutmak için belirteci yenilemeniz gerekiyorsa (bağlantı sırasında süresi dolarsa), bunu bu işlevin içinden yapın ve güncelleştirilmiş belirteci döndürün.
Standart Web API 'Lerinde, taşıyıcı belirteçleri bir HTTP üst bilgisinde gönderilir. Ancak, SignalR bazı aktarımlar kullanılırken tarayıcılarda bu üst bilgileri ayarlayamamalıdır. WebSockets ve Server-Sent olaylarını kullanırken, belirteç bir sorgu dizesi parametresi olarak iletilir.
Yerleşik JWT kimlik doğrulaması
Sunucusunda, 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 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.
}
Ingilizce dışındaki dillere çevrilmiş kod açıklamalarını görmek isterseniz, Bu GitHub tartışma sorununubize tanıyın.
Not
Sorgu dizesi tarayıcı API 'SI sınırlamaları nedeniyle WebSockets ve Server-Sent olaylarıyla bağlantı kurulurken tarayıcılarda kullanılır. HTTPS kullanılırken sorgu dizesi değerleri TLS bağlantısıyla korunmuş hale getirilir. Ancak, birçok sunucu günlük sorgu dizesi değerlerini günlüğe kaydeder. Daha fazla bilgi için SignalR ASP.NET Core güvenlik konuları bölümüne bakın. 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 , PostConfigureOptions<TOptions> projeye bir 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;
}
}
};
}
}
Startup.ConfigureServicesKimlik doğrulaması () için Hizmetler AddAuthentication ve sunucu için kimlik doğrulama işleyicisi () eklendikten sonra hizmeti kaydetme Identity AddIdentityServerJwt :
services.AddAuthentication()
.AddIdentityServerJwt();
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,
ConfigureJwtBearerOptions>());
Cookies ile taşıyıcı belirteçleri karşılaştırması
Cookie, tarayıcılara özeldir. Diğer istemci türlerinden gönderilmesi, taşıyıcı belirteçlerinin gönderilmesine kıyasla karmaşıklık ekler. Sonuç olarak, cookie uygulamanın yalnızca tarayıcı istemcisinden kullanıcıların kimliğini doğrulaması gerekmediği takdirde kimlik doğrulaması önerilmez. Taşıyıcı belirteç kimlik doğrulaması, tarayıcı istemcisi dışındaki istemcileri kullanırken önerilen yaklaşımdır.
Windows kimlik doğrulaması
uygulamanızda Windows kimlik doğrulaması yapılandırıldıysa, SignalR hub 'ları güvenli hale getirmek için bu kimliği kullanabilir. Ancak, bireysel 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ı" talebi sağlamaz. SignalR Kullanıcı adını öğrenmek için talebi kullanır.
IUserIdProviderKullanıcı tarafından tanımlayıcı olarak kullanılacak taleplerden birini uygulayan ve alan yeni bir sınıf ekleyin. örneğin, "ad" talebini kullanmak için (formdaki kullanıcı adı Windows [Domain]\[Username] ), 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 ' den herhangi bir değer User (Windows sıd 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ı için tasarlanan bir ileti farklı bir kullanıcıya gidiyor olabilir.
Bu bileşeni yönteminizin içine kaydedin Startup.ConfigureServices .
public void ConfigureServices(IServiceCollection services)
{
// ... other services ...
services.AddSignalR();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
}
.net istemcisinde, usedefaultcredentials özelliği ayarlanarak Windows kimlik doğrulamasının etkinleştirilmesi gerekir:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chathub", options =>
{
options.UseDefaultCredentials = true;
})
.Build();
Windows kimlik doğrulaması ınternet Explorer 'da ve Microsoft Edge desteklenir, ancak tüm tarayıcılarda desteklenmez. örneğin, Chrome ve Safari 'de Windows kimlik doğrulaması ve WebSockets kullanma girişimi başarısız olur. Windows kimlik doğrulaması başarısız olduğunda, istemci çalışmayabilir.
Kimlik işlemeyi özelleştirmek için talepler kullanma
Kullanıcıların kimliğini doğrulayan bir uygulama, Kullanıcı SignalR taleplerinden Kullanıcı kimliklerini türetebilir. SignalRKullanıcı kimliklerini nasıl oluşturduğunu belirtmek için, IUserIdProvider uygulamayı uygulayın ve kaydedin.
Örnek kod, tanımlama özelliği 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ı için tasarlanan bir ileti farklı bir kullanıcıya gidiyor olabilir.
public class EmailBasedUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
hesap kaydı, ASP.NET ıdentity veritabanına türünde bir talep ekler ClaimsTypes.Email .
// 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 ' ye kaydedin Startup.ConfigureServices .
services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();
Kullanıcılara, hub 'lara ve hub yöntemlerine erişim 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ı gerektirmek için, Yetkilendir özniteliğini hub 'a uygulayın:
[Authorize]
public class ChatHub: Hub
{
}
[Authorize]Yalnızca belirli Yetkilendirme ilkeleriyleeşleşen kullanıcılara erişimi kısıtlamak için özniteliğinin Oluşturucu bağımsız değişkenlerini ve özelliklerini kullanabilirsiniz. Örneğin, adlı bir özel yetkilendirme ilkeniz varsa, MyAuthorizationPolicy aşağıdaki kodu kullanarak yalnızca bu ilkeyle eşleşen kullanıcıların hub 'a erişmesini sağlayabilirsiniz:
[Authorize("MyAuthorizationPolicy")]
public class ChatHub : Hub
{
}
Tek bir hub yöntemi [Authorize] özniteliğe de uygulanmış olabilir. Geçerli Kullanıcı, yönteme uygulanan ilkeyle eşleşmezse, çağırana 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 HubCallerContext Çağrılan hub yönteminin adını ve hub yönteminin bağımsız değişkenlerini içerir.
Azure Active Directory aracılığıyla birden çok kuruluşun oturum açmasına izin veren bir sohbet odası örneğini göz önünde bulundurun. Microsoft hesabı olan herkes sohbet için oturum açabilir, ancak yalnızca sahip olunan kuruluşun üyeleri kullanıcıları yasaklatabilecek veya kullanıcıların sohbet geçmişlerini görüntüleyebilmelidir. Ayrıca, belirli kullanıcılardan belirli işlevleri kısıtlamak isteyebilirsiniz. ASP.NET Core 3,0 ' deki güncelleştirilmiş özellikleri kullanarak bu tamamen mümkündür. Nasıl DomainRestrictedRequirement özel olarak işlev gördüğüne göz önünde kalın IAuthorizationRequirement . HubInvocationContextKaynak parametresi geçirilmeye hazır olduğuna göre, iç mantık hub 'ın çağrıldığı bağlamı inceleyebilir ve kullanıcının bireysel hub yöntemlerini yürütmesine izin verirken kararlar verebilir.
[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));
}
}
İçinde Startup.ConfigureServices , DomainRestrictedRequirement ilkeyi oluşturmak için özel gereksinimi bir parametre olarak sağlayarak yeni ilkeyi ekleyin DomainRestricted .
public void ConfigureServices(IServiceCollection services)
{
// ... other services ...
services
.AddAuthorization(options =>
{
options.AddPolicy("DomainRestricted", policy =>
{
policy.Requirements.Add(new DomainRestrictedRequirement());
});
});
}
Yukarıdaki örnekte, DomainRestrictedRequirement sınıfı, IAuthorizationRequirement Bu gereksinim için hem bir ve kendisi olur AuthorizationHandler . Bu iki bileşeni birbirinden ayrı ayrı sınıflara bölmek kabul edilebilir. Örneğin yaklaşımın bir avantajı, AuthorizationHandler gereksinim ve işleyicinin aynı şey olduğu için başlangıç sırasında ekleme zorunluluktur.