我遵循以下解决方案并将代码移至库项目中。如果我在 Blazor Server 项目中有集线器连接,它可以正常工作,但在移动到库项目时则不起作用:
我在 Library Razor 项目中有以下服务,它保存集线器连接设置的实现,并在两个 Blazor 服务器应用程序之间共享:
public class UserHubService : IAsyncDisposable
{
private HubConnection? _hubConnection;
public async Task<bool> InitializeHubAsync(string baseUrl, string hubPath, string subscribeToGroup, Guid? siteId, string userName)
{
UserName = userName;
var uri = new Uri(new Uri(baseUrl), hubPath);
//_hubConnection = new HubConnectionBuilder()
// .WithUrl(new Uri(new Uri(baseUrl), hubPath))
// .Build();
_hubConnection = new HubConnectionBuilder()
.WithUrl(uri, opti =>
{
if (_httpContextAccessor.HttpContext != null)
foreach (var c in _httpContextAccessor.HttpContext.Request.Cookies)
{
opti.Cookies.Add(new Cookie(c.Key, c.Value)
{
Domain = uri.Host, // Set the domain of the cookie
Path = "/" // Set the path of the cookie
});
}
})
.Build();
_hubConnection.On<Guid?, ProductModel, string>(SignalR_Method.TouchProductReceiveNotification, async (siteID, product, messageType) =>
{
if (_subscribedMessageTypes.Contains(messageType))
{
await HandleNotificationAsync(siteID, product, messageType);
}
});
try
{
await _hubConnection.StartAsync();
await _hubConnection.InvokeAsync(subscribeToGroup, siteId);
return true;
}
catch (Exception ex)
{
return false;
// Handle exception (e.g., log it)
}
}
}
上述服务被注入页面并从服务器应用程序调用:
await ProductNotificationHubService.InitializeHubAsync(baseUrl, hubPath, SignalR_Method.SubscribeToTouchSiteGroup, Site.Site_ID, _userInfo.UserName)
以下是 Library razor 项目中的中心:
[Authorize]
public class NotificationHub : Hub
{
private static readonly ConcurrentDictionary<string, List<string>> UserConnections = new();
[Authorize]
public override Task OnConnectedAsync()
{
var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value;
//var userName = Context.User?.Identity?.Name; // Assuming the username is stored in the Name claim
if (!string.IsNullOrEmpty(userEmail))
{
UserConnections.AddOrUpdate(
userEmail,
new List<string> { Context.ConnectionId }, // Add a new list with the current connection ID
(key, existingConnections) =>
{
if (!existingConnections.Contains(Context.ConnectionId))
{
existingConnections.Add(Context.ConnectionId); // Add the connection ID to the existing list
}
return existingConnections;
});
}
return base.OnConnectedAsync();
}
[Authorize]
public override Task OnDisconnectedAsync(Exception exception)
{
var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value;
var connectionID = Context.ConnectionId;
if (!string.IsNullOrEmpty(userEmail))
{
if (UserConnections.TryGetValue(userEmail, out var connections))
{
// Remove the specific connection ID
connections.Remove(Context.ConnectionId);
// If no more connections exist for this user, remove the user entry from the dictionary
if (connections.Count == 0)
{
UserConnections.TryRemove(userEmail, out _);
}
}
}
return base.OnDisconnectedAsync(exception);
}
}
我已确认 cookie 已正确设置:
_hubConnection = new HubConnectionBuilder()
.WithUrl(uri, opti =>
{
if (_httpContextAccessor.HttpContext != null)
foreach (var c in _httpContextAccessor.HttpContext.Request.Cookies)
{
opti.Cookies.Add(new Cookie(c.Key, c.Value)
{
Domain = uri.Host, // Set the domain of the cookie
Path = "/" // Set the path of the cookie
});
}
})
.Build();
在program.cs文件中:
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
options.RequireAuthenticatedSignIn = true;
}).AddCookie(options =>
{
options.LoginPath = "/Account/Login/";
options.LogoutPath = "/Account/Logout/";
options.AccessDeniedPath = "/Account/AccessDenied";
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.Cookie.HttpOnly = true;
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromSeconds(30);
}).AddIdentityCookies();
app.UseAuthentication();
app.UseAuthorization();
连接成功并已连接,但是我遇到的问题是下面一行中的 userEmail 始终为空:
var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value;
我找到了上下文为空的原因。我正在连接到 hubconnection 以从一个服务器应用程序发送到另一个服务器应用程序。现在,我已经输入了另一个应用程序的 URL,这意味着用户未在目标 URL 上进行身份验证。当我将 url 更改为源时,它会显示上下文。
现在我的问题是,如何验证向其他应用程序发送更新的用户是否是注册用户以及安全信号器连接。
根据官方建议,我们最好使用Bearer token认证。
当然,我们也可以使用Cookie认证,但是需要注意浏览器的限制,不同域名之间不能共享cookie。
这是给您的示例代码。
// Retrieve token from localStorage
var token = await _localStorageService.GetItemAsync("jwt_token");
var baseUrl = "https://localhost:7135"; // Replace with your actual domain
var hubPath = "/notificationHub"; // Path to your hub
_hubConnection = new HubConnectionBuilder()
.WithUrl(new Uri(new Uri(baseUrl), hubPath), options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.WithAutomaticReconnect()
.Build();