SignalR dotnet 核心认证
SignalR dotnet core authentication
我正在使用 Microsoft.AspNetCore.SignalR
nuget package with Bazinga.AspNetCore.Authentication.Basic
,它将基本身份验证添加到 dotnet 核心。我的 C# SignalR 客户端在没有身份验证时连接,但是当我添加 AuthorizeAttribute
它通过 http 和 http 请求连接时 header 成功通过身份验证但 Socket 未进行身份验证可能是因为没有 header 在套接字消息中。
所以我想知道我应该如何将令牌或其他东西传递给经过身份验证的套接字连接,或者是否有我可以遵循的示例代码。我想我应该将随机令牌传递给经过身份验证的用户,而用户需要不断地在消息中传递令牌。
Client project, Server project
服务器:
using System.Threading.Tasks;
using Bazinga.AspNetCore.Authentication.Basic;
using Domainlogic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace API
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin();
}));
services.AddSignalR();
services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
.AddBasicAuthentication(credentials => Task.FromResult(
credentials.username == "SomeUserName"
&& credentials.password == "SomePassword"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
app.UseCors(CorsConstants.AnyOrigin);
app.UseFileServer();
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
app.UseAuthentication();
}
}
}
服务器中心:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
namespace Domainlogic
{
public class MessagePayload
{
public string Name { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
[Authorize]
public class MessageHub : Hub
{
// connected IDs
private static readonly HashSet<string> ConnectedIds = new HashSet<string>();
public override async Task OnConnectedAsync()
{
ConnectedIds.Add(Context.ConnectionId);
await Clients.All.SendAsync("SendAction", "joined", ConnectedIds.Count);
}
public override async Task OnDisconnectedAsync(Exception ex)
{
ConnectedIds.Remove(Context.ConnectionId);
await Clients.All.SendAsync("SendAction", "left", ConnectedIds.Count);
}
public async Task Send(MessagePayload message)
{
await Clients.All.SendAsync("SendMessage", message);
}
}
}
客户:
using System;
using System.Net;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
namespace SignalRClient
{
public class MessagePayload
{
public string Name { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
class Program
{
public static string Base64Encode(string plainText) {
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
static void Main(string[] args)
{
var credential = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("SomeUserName" + ":" + "SomePassword"));
//Set connection
var connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/chat", options =>
{
options.Headers.Add("Authorization", $"Basic {credential}");
})
.AddJsonProtocol()
.Build();
connection.On<MessagePayload>("SendMessage", param =>
{
Console.WriteLine(param.Message);
});
connection.StartAsync().Wait();
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromSeconds(3);
int i = 0;
var timer = new System.Threading.Timer((e) =>
{
connection.InvokeAsync<MessagePayload>("Send", new MessagePayload()
{
Message = "Some message: " + i++
});
}, null, startTimeSpan, periodTimeSpan);
Console.Read();
connection.StopAsync();
}
}
}
为了解决这个问题,我必须使用字典将用户的令牌和 connectionId 存储在服务器缓存中。那是因为在集线器中我无法控制会话。
因此,每次用户连接到我公开的集线器和接收用户的 connectionId 和令牌的端点。当集线器处理请求时,我通过用户认证的连接知道。
控制器
[HttpPost]
[Authorize]
[Route("{connectionId}")]
public IActionResult Post(string connectionId)
{
this.hubConnectionService.AddConnection(connectionId, this.workContext.CurrentUserId);
return this.Ok();
}
枢纽
public override Task OnDisconnectedAsync(Exception exception)
{
this.hubConnectionService.RemoveConnection(this.Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
感谢 GitHub 上的 "davidfowl",解决方案正在 UseAuthentication
移动到 UseSignalR
之上。
来源:https://github.com/aspnet/SignalR/issues/2316
而不是:
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
app.UseAuthentication();
使用这个:
app.UseAuthentication();
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
我正在使用 Microsoft.AspNetCore.SignalR
nuget package with Bazinga.AspNetCore.Authentication.Basic
,它将基本身份验证添加到 dotnet 核心。我的 C# SignalR 客户端在没有身份验证时连接,但是当我添加 AuthorizeAttribute
它通过 http 和 http 请求连接时 header 成功通过身份验证但 Socket 未进行身份验证可能是因为没有 header 在套接字消息中。
所以我想知道我应该如何将令牌或其他东西传递给经过身份验证的套接字连接,或者是否有我可以遵循的示例代码。我想我应该将随机令牌传递给经过身份验证的用户,而用户需要不断地在消息中传递令牌。
Client project, Server project
服务器:
using System.Threading.Tasks;
using Bazinga.AspNetCore.Authentication.Basic;
using Domainlogic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace API
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin();
}));
services.AddSignalR();
services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
.AddBasicAuthentication(credentials => Task.FromResult(
credentials.username == "SomeUserName"
&& credentials.password == "SomePassword"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
app.UseCors(CorsConstants.AnyOrigin);
app.UseFileServer();
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
app.UseAuthentication();
}
}
}
服务器中心:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
namespace Domainlogic
{
public class MessagePayload
{
public string Name { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
[Authorize]
public class MessageHub : Hub
{
// connected IDs
private static readonly HashSet<string> ConnectedIds = new HashSet<string>();
public override async Task OnConnectedAsync()
{
ConnectedIds.Add(Context.ConnectionId);
await Clients.All.SendAsync("SendAction", "joined", ConnectedIds.Count);
}
public override async Task OnDisconnectedAsync(Exception ex)
{
ConnectedIds.Remove(Context.ConnectionId);
await Clients.All.SendAsync("SendAction", "left", ConnectedIds.Count);
}
public async Task Send(MessagePayload message)
{
await Clients.All.SendAsync("SendMessage", message);
}
}
}
客户:
using System;
using System.Net;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
namespace SignalRClient
{
public class MessagePayload
{
public string Name { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
class Program
{
public static string Base64Encode(string plainText) {
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
static void Main(string[] args)
{
var credential = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("SomeUserName" + ":" + "SomePassword"));
//Set connection
var connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/chat", options =>
{
options.Headers.Add("Authorization", $"Basic {credential}");
})
.AddJsonProtocol()
.Build();
connection.On<MessagePayload>("SendMessage", param =>
{
Console.WriteLine(param.Message);
});
connection.StartAsync().Wait();
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromSeconds(3);
int i = 0;
var timer = new System.Threading.Timer((e) =>
{
connection.InvokeAsync<MessagePayload>("Send", new MessagePayload()
{
Message = "Some message: " + i++
});
}, null, startTimeSpan, periodTimeSpan);
Console.Read();
connection.StopAsync();
}
}
}
为了解决这个问题,我必须使用字典将用户的令牌和 connectionId 存储在服务器缓存中。那是因为在集线器中我无法控制会话。
因此,每次用户连接到我公开的集线器和接收用户的 connectionId 和令牌的端点。当集线器处理请求时,我通过用户认证的连接知道。
控制器
[HttpPost]
[Authorize]
[Route("{connectionId}")]
public IActionResult Post(string connectionId)
{
this.hubConnectionService.AddConnection(connectionId, this.workContext.CurrentUserId);
return this.Ok();
}
枢纽
public override Task OnDisconnectedAsync(Exception exception)
{
this.hubConnectionService.RemoveConnection(this.Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
感谢 GitHub 上的 "davidfowl",解决方案正在 UseAuthentication
移动到 UseSignalR
之上。
来源:https://github.com/aspnet/SignalR/issues/2316
而不是:
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
app.UseAuthentication();
使用这个:
app.UseAuthentication();
app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });