C# 使用Azure AD进行身份验证时,.NET Core不会在中间件中显示用户声明

C# 使用Azure AD进行身份验证时,.NET Core不会在中间件中显示用户声明,c#,authentication,asp.net-core,azure-active-directory,asp.net-core-identity,C#,Authentication,Asp.net Core,Azure Active Directory,Asp.net Core Identity,我有一个多租户.NET核心web应用程序,当前用户的租户通过中间件解析。特别是,租户通过一个名为 要使用此库,请将此行放入ConfigureServices(): 这将导致在中间件中执行以下方法,该方法解析当前用户的租户: public async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context) { //whatever you need to do to figure out the t

我有一个多租户.NET核心web应用程序,当前用户的租户通过中间件解析。特别是,租户通过一个名为

要使用此库,请将此行放入
ConfigureServices()

这将导致在中间件中执行以下方法,该方法解析当前用户的租户:

public async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)
{
    //whatever you need to do to figure out the tenant goes here.
}
到目前为止,我们一直在使用.NET Identity platform对用户进行身份验证,并将用户数据存储在应用程序的数据库中。然而,我们的一个新租户希望能够通过SSO对其用户进行身份验证

我已经了解了SSO的大部分内容——我正在使用Azure广告登录用户,以及。简而言之,
ConfigureServices
中的此代码添加了身份和AzureAD身份验证:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // rest of the code is omitted for brevity 

    services.AddIdentity<ApplicationUser, ApplicationRole>(config =>
    {
        config.User.RequireUniqueEmail = true;
        config.Password.RequiredLength = 12;
    }).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        .AddAzureAD(options => _configuration.Bind("AzureAd", options)).AddCookie();

    // policy gets user past [Authorize] if they are signed in with Identity OR Azure AD
    services.AddMvc(options =>
    {
        var policy = new AuthorizationPolicyBuilder(
                 AzureADDefaults.AuthenticationScheme,
                 IdentityConstants.ApplicationScheme
               ).RequireAuthenticatedUser()
               .Build();
           options.Filters.Add(new AuthorizeFilter(policy));
    });

}
公共IServiceProvider配置服务(IServiceCollection服务)
{
//为了简洁起见,代码的其余部分被省略
services.AddIdentity(配置=>
{
config.User.RequireUniqueEmail=true;
config.Password.RequiredLength=12;
}).AddEntityFrameworkStores().AddDefaultTokenProviders();
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options=>_configuration.Bind(“AzureAd”,options)).AddCookie();
//如果用户使用Identity或Azure AD登录,则策略会让用户通过[授权]
services.AddMvc(选项=>
{
var policy=new AuthorizationPolicyBuilder(
AzureADDefaults.AuthenticationScheme,
IdentityConstants.ApplicationScheme
).RequireAuthenticatedUser()文件
.Build();
options.Filters.Add(新的授权过滤器(策略));
});
}
使用Identity时,我已经能够使用UserManager解析用户的租户,如下所示:

private readonly Tenant _tenant;

public HomeController(Tenant tenant)
{
   _tenant = tenant;
}
public async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)
{
    TenantContext<Tenant> tenantContext = new TenantContext<Tenant>(new ApplicationTenant());
    if (context.User.Identity.IsAuthenticated)
    {
        var user = await _userManager.Users
                              .Include(x => x.Tenant)
                              .FirstOrDefaultAsync(x => x.UserName == email);
        if (user?.Tenant != null)
        {
            tenantContext = new TenantContext<Tenant>(user.Tenant);
            _logger.LogDebug("The current tenant is " + user.Tenant.Name);
            return await Task.FromResult(tenantContext);
        }
    }

    return await Task.FromResult(tenantContext);
}
公共异步任务ResolveAsync(HttpContext上下文) { TenantContext TenantContext=新的TenantContext(new ApplicationTenant()); if(context.User.Identity.IsAuthenticated) { var user=await\u userManager.Users .Include(x=>x.Tenant) .FirstOrDefaultAsync(x=>x.UserName==电子邮件); if(用户?.Tenant!=null) { 租户上下文=新租户上下文(user.Tenant); _logger.LogDebug(“当前租户是”+user.tenant.Name); 返回wait Task.FromResult(租户上下文); } } 返回wait Task.FromResult(租户上下文); } 我的计划是修改这段代码,以便抓取当前用户的声明,这可以用来推断用户属于哪个租户但是,当通过Azure AD验证用户时,
HttpContext.user
在中间件中始终为空,尽管用户已登录。它不是null,但是
HttpContext.User.Identity.IsAuthenticated
始终为false,并且
HttpContext.User.Claims
为空。我只看到
HttpContext.User的值,一旦路由完成并且代码到达控制器,就会填充该值。

我尝试过以几乎所有可行的方式重新组织中间件,但没有结果。让我困惑的是,当用户通过身份验证时,
HttpContext.User
被填充到租户解析程序中。考虑到这一点,我不确定如何在通过Azure AD进行身份验证时访问中间件中的用户声明

我能想到的最佳解决方案是,通过调用通过声明解析租户的方法,修改当前租户被注入代码中的每个实例。如果租户在受[Authorize]属性限制的区域中为空,则意味着用户已通过Azure AD登录,这将允许我查看他们的声明。然而,我无法访问中间件中用户的声明,这让我非常困扰,因为我不确定这里到底发生了什么



由于您试图从自定义组件访问HttpContext,因此您需要将
HttpContextAccessor
添加到您的服务集合中,如下所示:

services.AddHttpContextAccessor();
现在,您可以使用依赖项注入根据需要解析
HttpContext
。这可能有帮助,也可能没有帮助,这取决于您对所使用的中间件的控制程度


值得一提的是,仅使用MSAL而不使用额外的第三方中间件对AAD进行身份验证并没有什么问题。祝你好运

我怀疑您可能在.NET Core 2.0上运行并遇到了这个问题。

非常感谢!但恐怕这不能解决问题。。。添加HttpContextAccessor不仅没有效果,而且我相信对AddMultitenance()的调用。我不确定在使用Azure AD时,用户的身份验证cookie在管道中的何处转化为他们的声明。它肯定比使用Identity时要远得多。这方面运气好吗?
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // rest of the code is omitted for brevity 

    services.AddIdentity<ApplicationUser, ApplicationRole>(config =>
    {
        config.User.RequireUniqueEmail = true;
        config.Password.RequiredLength = 12;
    }).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        .AddAzureAD(options => _configuration.Bind("AzureAd", options)).AddCookie();

    // policy gets user past [Authorize] if they are signed in with Identity OR Azure AD
    services.AddMvc(options =>
    {
        var policy = new AuthorizationPolicyBuilder(
                 AzureADDefaults.AuthenticationScheme,
                 IdentityConstants.ApplicationScheme
               ).RequireAuthenticatedUser()
               .Build();
           options.Filters.Add(new AuthorizeFilter(policy));
    });

}
public async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)
{
    TenantContext<Tenant> tenantContext = new TenantContext<Tenant>(new ApplicationTenant());
    if (context.User.Identity.IsAuthenticated)
    {
        var user = await _userManager.Users
                              .Include(x => x.Tenant)
                              .FirstOrDefaultAsync(x => x.UserName == email);
        if (user?.Tenant != null)
        {
            tenantContext = new TenantContext<Tenant>(user.Tenant);
            _logger.LogDebug("The current tenant is " + user.Tenant.Name);
            return await Task.FromResult(tenantContext);
        }
    }

    return await Task.FromResult(tenantContext);
}