Asp.net core Autofac多租户解析来自当前用户的租户’;校长

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:环境

我有一个.NET 5 ASP.NET Core API,我正在其中尝试使用Autofac.AspNetCore.Multitenant v4.0.1设置Multitenance。我已经实现了一个TenantIdentificationStrategy,它根据当前用户主体的声明来识别租户问题在于,在Autofac解析租户标识符时,用户似乎尚未填充。

这里有Autofac文档 国家:

iTenantIdentificationsStrategy允许您检索租户 适用于您的应用程序的任意位置的ID:环境 变量,当前用户主体上的角色,传入请求 价值,或其他任何地方

由于这句话,我认为这应该是可行的,所以我想知道我是否做错了什么,或者框架上是否有bug,或者文档是否声明了框架不支持的内容

有什么办法让它工作吗?

找到以下我的应用程序配置:

节目

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;
    }
}