Я пытаюсь получить своего вошедшего в систему пользователя и идентификатор соединения, чтобы сопоставить пользователя соответствующим образом.
Ниже представлен мой файл program.cs:
Ниже представлен мой ItemAccessHub:
Ниже представлен мой code Middleware:
Однако мой контекст всегда равен null:
Я просмотрел всю документацию Microsoft, но не смог заставить ее работать. Мне нужны данные контекстной информации (Авторизованный пользователь) для отслеживания и управления идентификатором подключения.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSignalR();
// Add services to the container.
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.TryAddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
builder.Services.AddAuthorizationCore();
builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
options.RequireAuthenticatedSignIn = true;
}).AddIdentityCookies();
//Database Connection
DatabaseConnection connectionString = new(builder.Configuration.GetConnectionString("OffDatabase") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."));
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(DatabaseConnection.ConnectionString), ServiceLifetime.Transient);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddHttpContextAccessor();
builder.Services.AddIdentityCore<ApplicationUser>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 5;
options.Password.RequireNonAlphanumeric = false;
options.SignIn.RequireConfirmedEmail = true;
options.SignIn.RequireConfirmedAccount = true;
}).AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders()
.AddSignInManager<SignInManager<ApplicationUser>>();
builder.Services.AddCors();
builder.Services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/Account/Login";
options.LogoutPath = "/Account/Logout";
options.AccessDeniedPath = "/Account/AccessDenied";
options.ExpireTimeSpan = TimeSpan.FromDays(7);
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.SlidingExpiration = true;
});
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.WebHost.UseStaticWebAssets();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseMiddleware<BlazorCookieLoginMiddleware>();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddAdditionalAssemblies(typeof(AuthenticationLibrary._Imports).Assembly)
.AddInteractiveServerRenderMode();
app.MapAdditionalIdentityEndpoints();
app.MapControllers();
app.MapHub<ItemAccessHub>("/itemaccesshub");
app.Run();
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;
using System.Security.Claims;
using System.Web.Mvc;
namespace POSFlow_Admin.Hubs
{
[Authorize]
public class ItemAccessHub : Hub
{
private static readonly ConcurrentDictionary<string, (string UserEmail, string Module, string UserName)> RowLocks = new();
private static readonly ConcurrentDictionary<string, string> UserConnections = new();
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[userEmail] = Context.ConnectionId;
}
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value;
if (!string.IsNullOrEmpty(userEmail) && UserConnections.ContainsKey(userEmail))
{
// Remove the user's connection ID
UserConnections.TryRemove(userEmail, out _);
var lockedRows = RowLocks.Where(x => x.Value.UserEmail == userEmail).Select(x => x.Key).ToList();
foreach (var rowId in lockedRows)
{
RowLocks.TryRemove(rowId, out _);
Clients.Others.SendAsync("RowUnlocked", rowId);
}
}
return base.OnDisconnectedAsync(exception);
}
public Task LockRow(string rowId, string module)
{
var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value;
var userName = Context.User?.Identity?.Name;
if (!string.IsNullOrEmpty(userEmail) && !RowLocks.ContainsKey(rowId))
{
RowLocks[rowId] = (userEmail, module, userName);
return Clients.Others.SendAsync("RowLocked", rowId, userName);
}
return Task.CompletedTask;
}
public Task UnlockRow(string rowId)
{
var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value;
if (!string.IsNullOrEmpty(userEmail) && RowLocks.ContainsKey(rowId))
{
if (RowLocks.TryGetValue(rowId, out var lockInfo) && lockInfo.UserEmail == userEmail)
{
RowLocks.TryRemove(rowId, out _);
return Clients.Others.SendAsync("RowUnlocked", rowId);
}
}
return Task.CompletedTask;
}
}
}
public class BlazorCookieLoginMiddleware
{
public static IDictionary<Guid, LoginInfo> Logins { get; private set; }
= new ConcurrentDictionary<Guid, LoginInfo>();
private readonly RequestDelegate _next;
public BlazorCookieLoginMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, SignInManager<ApplicationUser> signInMgr)
{
// Skip Blazor-related paths
if (context.Request.Path.Value.StartsWith("/_blazor") || context.Request.Path.Value.StartsWith("/_framework"))
{
await _next(context);
return;
}
if (context.Request.Path == "/login" && context.Request.Query.ContainsKey("key"))
{
var key = Guid.Parse(context.Request.Query["key"]);
var info = Logins[key];
var result = await signInMgr.PasswordSignInAsync(info.Email, info.Password, false, lockoutOnFailure: true);
info.Password = null;
if (result.Succeeded)
{
Logins.Remove(key);
context.Response.Redirect("/");
return;
}
else if (result.RequiresTwoFactor)
{
//TODO: redirect to 2FA razor component
context.Response.Redirect("/loginwith2fa/" + key);
return;
}
else
{
//TODO: Proper error handling
context.Response.Redirect("/loginfailed");
return;
}
}
else if (context.Request.Path == "/logout" && context.Request.Query.ContainsKey("key"))
{
var key = Guid.Parse(context.Request.Query["key"]);
await signInMgr.SignOutAsync();
Logins.Remove(key);
context.Response.Redirect("/");
return;
}
else
{
await _next.Invoke(context);
return;
}
}
}
Ким
Вопрос задан7 августа 2024 г.