Asp.net core Autofac多租户解析来自当前用户的租户’;校长
我有一个.NET 5 ASP.NET Core API,我正在其中尝试使用Autofac.AspNetCore.Multitenant v4.0.1设置Multitenance。我已经实现了一个TenantIdentificationStrategy,它根据当前用户主体的声明来识别租户问题在于,在Autofac解析租户标识符时,用户似乎尚未填充。 这里有Autofac文档 国家: iTenantIdentificationsStrategy允许您检索租户 适用于您的应用程序的任意位置的ID:环境 变量,当前用户主体上的角色,传入请求 价值,或其他任何地方 由于这句话,我认为这应该是可行的,所以我想知道我是否做错了什么,或者框架上是否有bug,或者文档是否声明了框架不支持的内容 有什么办法让它工作吗? 找到以下我的应用程序配置: 节目Asp.net core Autofac多租户解析来自当前用户的租户’;校长,asp.net-core,asp.net-core-webapi,autofac,multi-tenant,Asp.net Core,Asp.net Core Webapi,Autofac,Multi Tenant,我有一个.NET 5 ASP.NET Core API,我正在其中尝试使用Autofac.AspNetCore.Multitenant v4.0.1设置Multitenance。我已经实现了一个TenantIdentificationStrategy,它根据当前用户主体的声明来识别租户问题在于,在Autofac解析租户标识符时,用户似乎尚未填充。 这里有Autofac文档 国家: iTenantIdentificationsStrategy允许您检索租户 适用于您的应用程序的任意位置的ID:环境
private static IHostBuilder CreateHostBuilder(string[] args)
=> Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder.UseStartup<Startup>();
});
编辑:租户身份验证策略
public class ClaimTenantIdentificationStrategy : ITenantIdentificationStrategy
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ITenantStore _tenantStore;
public ClaimTenantIdentificationStrategy(IHttpContextAccessor httpContextAccessor, ITenantStore tenantStore)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
_tenantStore = tenantStore ?? throw new ArgumentNullException(nameof(tenantStore));
}
public bool TryIdentifyTenant(out object tenantId)
{
var httpContext = _httpContextAccessor.HttpContext;
tenantId = httpContext?.Items["TenantId"];
if (tenantId != null)
return true;
var authorizationHeaderValue = httpContext?.Request?.Headers?[HeaderNames.Authorization];
if (authorizationHeaderValue.HasValue && !StringValues.IsNullOrEmpty(authorizationHeaderValue.Value))
{
var authorizationHeader = AuthenticationHeaderValue.Parse(authorizationHeaderValue);
var jwtTokenHandler = new JwtSecurityTokenHandler();
var jwtToken = jwtTokenHandler.ReadJwtToken(authorizationHeader.Parameter);
var tenantIdClaim = jwtToken.Claims
.FirstOrDefault(x => x.Type == "tenantId");
if (tenantIdClaim != null && _tenantStore.IsValidTenant(tenantIdClaim.Value))
{
tenantId = tenantIdClaim.Value;
httpContext.Items["TenantId"] = tenantId;
return true;
}
}
return false;
}
}
<> P>阅读AutoFoc文档时要考虑的是,Autofac支持任何应用程序类型——控制台应用程序、Windows服务、ASP.NET应用程序等等。所以当你看到租户可以来自用户的委托人。。。嗯,它可以。您的应用程序是否已准备就绪,取决于您的应用程序。控制台可能从当前正在执行的线程中获取主体,并且在应用程序运行时它已经存在 对于ASP.NET/ASP.NET核心应用程序,有一个中间件运行来验证入站令牌,将该令牌转换为主体,并将该主体附加到当前请求 但是如果你仔细想想。。。中间件具有需要从请求容器解析的依赖关系,在多租户系统中,这意味着我们需要在中间件管道中首先了解租户,而不是等到身份验证/授权位运行 这也是ASP.NETClassic中的一个问题。这对于ASP.NET核心来说并不新鲜。我也用自己的应用程序实现了这一点 如何解决这个问题在很大程度上取决于应用程序-您使用的令牌类型、验证令牌的成本、您希望在安全方面承担的风险等 在我最近的一次尝试中,我们使用了签名(但未加密)的JWT。其中一项索赔,
tid
,包含租户ID。我们所做的是决定对令牌执行一些手动工作-验证签名,然后直接从令牌中获取tid
声明。没有更多的解析,没有其他验证(受众等)。它需要快一点。然后,我们将租户ID缓存在HttpContext.Items
中,这样我们就不必再次查找它,这样就可以在需要在验证中间件之前运行的租户ID的任何其他地方使用它
有一个轻微的风险是,有人可以获得不同租户的管道来执行从初始请求到身份验证中间件,然后被身份验证中间件拒绝。我们对此没有意见,因为它仍然会很快拒绝未经授权的人。这对你来说可能没问题,所以你必须做出决定
但是,长短不一的是:由于在身份验证中间件运行之前需要租户ID的竞争条件,我们不得不想出其他办法。看来你也得想点别的办法了。谢谢你的回复,我可能想知道。我根据您的评论添加了一个潜在的解决方案。它看起来像你建议的那样吗?我不能承诺审查或批准你的租户ID策略,特别是因为它涉及一些安全风险。很抱歉,我不能,但希望你没有被阻止。如果是这样的话,请接受我的回答,这样我就可以获得帮助你的荣誉。
public class ClaimTenantIdentificationStrategy : ITenantIdentificationStrategy
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ClaimTenantIdentificationStrategy(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
public bool TryIdentifyTenant(out object tenantId)
{
tenantId = null;
var claimsPrincipal = _httpContextAccessor.HttpContext?.User;
var claimsIdentity = claimsPrincipal?.Identity;
if (claimsIdentity != null && claimsIdentity.IsAuthenticated)
{
var identifier = claimsPrincipal.FindFirst("tenantId")?.Value;
if (!string.IsNullOrEmpty(identifier))
tenantId = identifier;
}
return tenantId != null;
}
}
public class ClaimTenantIdentificationStrategy : ITenantIdentificationStrategy
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ITenantStore _tenantStore;
public ClaimTenantIdentificationStrategy(IHttpContextAccessor httpContextAccessor, ITenantStore tenantStore)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
_tenantStore = tenantStore ?? throw new ArgumentNullException(nameof(tenantStore));
}
public bool TryIdentifyTenant(out object tenantId)
{
var httpContext = _httpContextAccessor.HttpContext;
tenantId = httpContext?.Items["TenantId"];
if (tenantId != null)
return true;
var authorizationHeaderValue = httpContext?.Request?.Headers?[HeaderNames.Authorization];
if (authorizationHeaderValue.HasValue && !StringValues.IsNullOrEmpty(authorizationHeaderValue.Value))
{
var authorizationHeader = AuthenticationHeaderValue.Parse(authorizationHeaderValue);
var jwtTokenHandler = new JwtSecurityTokenHandler();
var jwtToken = jwtTokenHandler.ReadJwtToken(authorizationHeader.Parameter);
var tenantIdClaim = jwtToken.Claims
.FirstOrDefault(x => x.Type == "tenantId");
if (tenantIdClaim != null && _tenantStore.IsValidTenant(tenantIdClaim.Value))
{
tenantId = tenantIdClaim.Value;
httpContext.Items["TenantId"] = tenantId;
return true;
}
}
return false;
}
}