我目前正在开发 ASP.NET Core 应用程序,我希望我的应用程序具有多租户功能,因此我添加了一个
TenancyProvider
服务,该服务被注入到我的 DbContext
中(以便我可以设置全局查询过滤器稍后):
public class TenancyProvider : ITenancyProvider
{
private readonly IHttpContextAccessor _context;
public TenancyProvider(IHttpContextAccessor context)
{
_context = context;
}
public string? AuthenticatedUser() => _context.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
我现在的问题是这个方法总是返回null。在调试模式下检查调用时,我发现
ClaimsPrincipal
不包含任何数据,即使在登录后也是如此。总是有 0 个声明,并且 IsAuthenticated
总是 false,所以我猜测 cookie 或从中读取数据存在问题。在 Chrome 开发工具中,我至少可以看到身份 Cookie 的存在。
对于身份验证,我使用
AspNetCore.Identity
。这是我的登录服务的当前代码:
[HttpPost]
public async Task<IActionResult> Login(LoginDTO loginDTO, string? ReturnUrl)
{
if (ModelState.IsValid == false)
{
ViewBag.Errors = ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage);
return View(loginDTO);
}
var user = await _userManager.FindByEmailAsync(loginDTO.Email);
if (user == null)
{
ModelState.AddModelError("Login", "Ungültiger Benutzer");
return View(loginDTO);
}
var result = await _signInManager.PasswordSignInAsync(loginDTO.Email, loginDTO.Password, isPersistent: false, lockoutOnFailure: false);
if (result.Succeeded)
{
//Return URL Redirect
if (!string.IsNullOrEmpty(ReturnUrl) && Url.IsLocalUrl(ReturnUrl))
{
return LocalRedirect(ReturnUrl);
}
return RedirectToAction("Index", "Orders");
}
ModelState.AddModelError("Login", "Ungültiger Benutzername oder Passwort");
return View(loginDTO);
}
我也尝试手动添加声明,但这对情况没有帮助,所以我再次将其删除。
这是我的
Program.cs
:
...
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ITenancyProvider, TenancyProvider>();
builder.Services.AddDbContext<ApplicationDbContext>(
options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddUserStore<UserStore<ApplicationUser, ApplicationRole, ApplicationDbContext, Guid>>()
.AddRoleStore<RoleStore<ApplicationRole, ApplicationDbContext, Guid>>();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
});
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
else
{
app.UseDeveloperExceptionPage();
}
app.UseHsts();
app.UseHttpsRedirection();
Rotativa.AspNetCore.RotativaConfiguration.Setup("wwwroot", wkhtmltopdfRelativePath: "Rotativa");
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
//app.MapRazorPages();
app.Run();
如果这只是我在这里遗漏的一件小事,我不会感到惊讶,但我现在已经花了几个小时在网络上搜索并尝试不同的解决方案。
如果您需要更多信息,请告诉我。我真的很期待您的答复。
干杯
更新 这是我在数据库上下文中调用租户提供程序的代码:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
private readonly string _tenantId;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, ITenancyProvider tenancyProvider) : base(options)
{
_tenantId = tenancyProvider.AuthenticatedUser();
}
...
这是一个屏幕截图,显示空的
Claims
列表,并且 IsAuthenticated
是 false
。当我截取屏幕截图时,用户已经登录。
应用程序的行为就像用户已登录一样。我可以访问如果未登录则无法访问的控制器和页面。
显然,问题是您无法从
HttpContext
构造函数中的 DbContext
访问用户。我在这里找到了一个适合我的用例的解决方案Stackoverflow 讨论。更多详细信息也可以在这个Github问题中找到。
您需要延迟获取用户信息,直到您真正需要它们为止。因此,我现在只需将
TenancyProvider
服务注入到我的构造函数中。我还引入了一个保存租户 ID 的属性,并且 getter 称为 TenancyProvider
。基本上就是这样。
我的
DbContext
:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
private readonly ITenancyProvider _tenancyProvider;
private ClientID Tenant
{
get
{
string? tenant = _tenancyProvider.AuthenticatedUser();
return tenant.IsNullOrEmpty() ? ClientID.CreateEmpty() : new ClientID(Guid.Parse(tenant));
}
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, ITenancyProvider tenancyProvider) : base(options)
{
_tenancyProvider = tenancyProvider;
}
//other members
}
我只是在我的
OnModelCreating()
中做
modelBuilder.Entity<Order>()
.HasQueryFilter(x => x.ClientID == Tenant);