我有一个内置在ASP.NET Core7中的API.我有一个读取消息并用消息响应的MediatR控制器.客户端正在从ReactJS网站连接.我计划在不同的域上部署多个客户端站点.
我正在try 找到最不容易被欺骗的方法来获取客户端连接的域.我能够知道哪些域被允许连接,所以如果有帮助,我可以使用API密钥.虽然安全性不是一个大问题,但它将有助于我的指标,以了解哪些域正在连接.
到目前为止,我已经考虑过使用API密钥、自定义头(Referrer头)或读取CORS头.
我有一个内置在ASP.NET Core7中的API.我有一个读取消息并用消息响应的MediatR控制器.客户端正在从ReactJS网站连接.我计划在不同的域上部署多个客户端站点.
我正在try 找到最不容易被欺骗的方法来获取客户端连接的域.我能够知道哪些域被允许连接,所以如果有帮助,我可以使用API密钥.虽然安全性不是一个大问题,但它将有助于我的指标,以了解哪些域正在连接.
到目前为止,我已经考虑过使用API密钥、自定义头(Referrer头)或读取CORS头.
我为你创建了一个非常简单的项目.请先判断一下检测结果.
我建议您可以生成临时安全密钥,并在创建集线器连接时将其附加.在我的样例代码中为:112233.
client-create-connetion个
var connection = new signalR.HubConnectionBuilder().withUrl("/mainHub?uid=jason&sid=test&api-key=112233").configureLogging(signalR.LogLevel.Trace).build();
然后你可以在OnConnectedAsync
中验证它,当然你可以把临时密钥存储在redis缓存或数据库中,当它被使用时,你可以删除它.它可以更安全地保护您的连接.
public override async Task OnConnectedAsync()
{
string? apiKey = Context?.GetHttpContext()?.Request.Query["api-key"].ToString();
// retrieve apiKey from db/redis and verify it
if (apiKey == "112233")
{
await base.OnConnectedAsync();
}
else
{
Context.Abort();
}
// Get HttpContext In asp.net core signalr
//IHttpContextFeature? hcf = this.Context?.Features?[typeof(IHttpContextFeature)] as IHttpContextFeature;
//HttpContext? hc = hcf?.HttpContext;
// userid
string? uid = Context?.GetHttpContext()?.Request.Query["uid"].ToString();
//string? uid = hc?.Request?.Path.Value?.Split(new string[] { "=", "" }, StringSplitOptions.RemoveEmptyEntries)[1].ToString();
// system id
string? sid = Context.GetHttpContext()?.Request.Query["sid"].ToString();
//string? sid = hc?.Request?.Path.Value?.Split(new string[] { "=", "" }, StringSplitOptions.RemoveEmptyEntries)[2].ToString();
string? userid = uid;
if (userid == null || userid.Equals(string.Empty))
{
Trace.TraceInformation("userid is required, can't connect signalr service");
return;
}
Trace.TraceInformation(userid + "connected");
// save connection
List<string>? existUserConnectionIds;
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
if (existUserConnectionIds == null)
{
existUserConnectionIds = new List<string>();
}
existUserConnectionIds.Add(Context!.ConnectionId);
ConnectedUsers.TryAdd(userid, existUserConnectionIds);
await base.OnConnectedAsync();
}
您可以使用var domain = context?.Request.Host.Host;
来检索HubLogFilter
内部的域.以下是为您提供的样例代码.
using Microsoft.AspNetCore.SignalR;
using System.Text.Json;
using System.Text;
namespace AspNetCore_SignalR
{
public class HubLogFilter : IHubFilter
{
private readonly ILogger<HubLogFilter> _logger;
private readonly HashSet<string> allowedDomains;
public HubLogFilter(ILogger<HubLogFilter> logger)
{
_logger = logger;
allowedDomains = new HashSet<string>
{
"a.com",
"b.com",
"localhost"
};
}
public async ValueTask<object?> InvokeMethodAsync(
HubInvocationContext invocationContext,
Func<HubInvocationContext, ValueTask<object?>> next
)
{
var startTime = DateTimeOffset.Now;
var context = invocationContext.Context.GetHttpContext();
var remoteIp = GetRemoteIpAddress(context);
var userId = invocationContext.Context.UserIdentifier ?? "NonUser";
// get the domain
var domain = context?.Request.Host.Host;
// check the domain
if (!IsDomainAllowed(domain))
{
_logger.LogWarning("Blocked WebSocket connection from {Domain}", domain);
context.Abort(); // if not allowed, abort the connection
return null;
}
try
{
var result = await next(invocationContext);
result = "failed";
// this means we can find the message sent from client
if (invocationContext.HubMethodArguments.Count > 0)
{
result = "succeed";
}
var elapsed = DateTimeOffset.Now - startTime;
var arguments = invocationContext.HubMethodArguments;
var contentLength = System.Text.Json.JsonSerializer.Serialize(arguments).Length;
//var contentLength = CalculateContentLength(invocationContext.HubMethodArguments);
_logger.LogInformation(
"WebSocket {RequestPath}/{HubMethodName} {RemoteIpAddress} {UserId} executed in {Elapsed:0.0000} ms with content length {ContentLength}",
context?.Request.Path,
invocationContext.HubMethodName,
remoteIp,
userId,
elapsed.TotalMilliseconds,
contentLength
);
return result;
}
catch (Exception ex)
{
_logger.LogError($"Exception calling '{invocationContext.HubMethodName}': {ex}");
throw;
}
}
private static string GetRemoteIpAddress(HttpContext? context)
{
var remoteIp = context?.Request.Headers?["X-Forwarded-For"];
if (string.IsNullOrEmpty(remoteIp))
{
remoteIp = context?.Connection.RemoteIpAddress?.ToString();
}
return remoteIp ?? "Unknown";
}
private static int CalculateContentLength(object?[] arguments)
{
var serializedArgs = JsonSerializer.Serialize(arguments);
return Encoding.UTF8.GetByteCount(serializedArgs);
}
private bool IsDomainAllowed(string domain)
{
return allowedDomains.Contains(domain);
}
}
}
My Program.cs个
builder.Services.AddSignalR(hubOptions => {
hubOptions.AddFilter<HubLogFilter>();
});