让我们使用netcoreapp3.1
从Visual Studio模板中获取Web应用程序。
它使用asp网络身份,例如单击“登录”按钮后,页面会刷新。
我想要实现的是拥有这样的SignalR Core
集线器方法
[HttpGet]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<bool> Login(string email, string password)
{
var result = await _signInManager.PasswordSignInAsync(email,
password, true, lockoutOnFailure: false).ConfigureAwait(false);
if (result.Succeeded)
{
return true;
}
....
....
}
很不幸,由于我的幼稚尝试,我会得到InvalidOperationException: Headers are read-only, response has already started.
堆栈跟踪以可怕的长结尾
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)
at Microsoft.AspNetCore.Http.ResponseCookies.Append(String key, String value, CookieOptions options)
at Microsoft.AspNetCore.CookiePolicy.ResponseCookiesWrapper.Append(String key, String value, CookieOptions options)
at Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager.AppendResponseCookie(HttpContext context, String key, String value, CookieOptions options)
at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.<HandleSignInAsync>d__25.MoveNext()
我发现,在类似的用例中,与HttpContext
进行交互是很常见的,但是由于ApplicationSignInManager
似乎相对独立,因此我无法找到如何在这种情况下发挥作用的方法。
我意识到从概念的角度来看,我很可能会遗漏一些东西,因此欢迎大家提供有关如何更接近预期目标的想法。
这里github issue似乎要进行描述,所以我可能需要考虑重新设计。
答案 0 :(得分:2)
如果切换到Bearer Token身份验证,则可以实现无控制器模型。
以下所有示例和代码均来自Authentication and authorization in ASP.NET Core SignalR。
打字稿连接
// Connect, using the token we got.
this.connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat", { accessTokenFactory: () => this.loginToken })
.build();
C#集线器生成器
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/myhub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(_myAccessToken);
})
.Build();
您提供的访问令牌功能在SignalR发出的每个HTTP请求之前被调用。如果您需要更新令牌以保持连接处于活动状态(因为它可能在连接期间过期),请从此函数内进行操作并返回更新的令牌。
在标准Web API中,承载令牌在HTTP标头中发送。但是,使用某些传输时,SignalR无法在浏览器中设置这些标头。使用WebSockets和服务器发送的事件时,令牌将作为查询字符串参数传输。要在服务器上支持此功能,需要其他配置:
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.
}
答案 1 :(得分:0)
似乎尝试在hub方法内执行登录没有任何意义。 将Controller添加到项目中并在其中执行登录操作比在SignalR Hub中执行更为方便。
我的动机是避免在项目内部使用Controller,因为不需要它们进行适当的应用程序设计,但这不能弥补随之而来的困难。
仅遵循this link并使用fetch(...)
就实现了我的目标。