首页 > 解决方案 > 使用 Azure AD 进行身份验证时,.NET Core 不会在中间件中显示用户声明

问题描述

我有一个多租户 .NET Core Web 应用程序,当前用户的租户通过中间件解析。特别是,租户通过一个名为SaasKit.Multitenancy的库来解决。

要使用这个库,你把这一行放在ConfigureServices()

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // (omitted for brevity)

    // The 'Tenant' type is what you resolve to, using 'ApplicationTenantResolver'
    services.AddMultitenancy<Tenant, ApplicationTenantResolver>();

    // ...
}

然后你把这条线Configure()添加到中间件管道中:

public void Configure(IApplicationBuilder app)
{
    // ...

    app.UseAuthentication();
    app.UseMultitenancy<Tenant>(); //this line
    app.UseMvc(ConfigureRoutes);

    // ...
}

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

public async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)
{
    //whatever you need to do to figure out the tenant goes here.
}

这允许将此方法的结果(无论哪个租户解析)注入到您想要的任何类中,如下所示:

private readonly Tenant _tenant;

public HomeController(Tenant tenant)
{
   _tenant = tenant;
}

到目前为止,我们一直在使用 .NET Identity 平台对用户进行身份验证,将用户数据存储在我们应用程序的数据库中。但是,我们的一个新租户希望能够通过 SSO 对其用户进行身份验证。

我已经弄清楚了 SSO 的大部分内容——我正在使用 Azure AD 登录用户,并且我组织的 Azure AD 租户将能够与他们的 Identity Provider 联合。简而言之,此代码ConfigureServices添加了 Identity 和 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));
    });

}

使用 Identity 时,我已经能够使用 UserManager 解析用户的租户,如下所示:

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

我的计划是修改此代码,以获取当前用户的声明,该声明可用于推断用户属于哪个租户。但是,当通过 Azure AD 对用户进行身份验证时HttpContext.User,尽管用户已登录,但中间件中始终为空。它不是 null,而是HttpContext.User.Identity.IsAuthenticated始终为 false 且HttpContext.User.Claims为空。HttpContext.User一旦路由完成并且代码到达控制器,我只会看到填充的值。

我尝试以几乎所有可行的方式重新组织中间件,但无济于事。令我困惑的是,HttpContext.User当用户使用 Identity 进行身份验证时,它会填充到租户解析器中。考虑到这一点,我不确定在通过 Azure AD 进行身份验证时如何访问中间件中的用户声明。

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


标签: c#authenticationasp.net-coreazure-active-directoryasp.net-core-identity

解决方案


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

services.AddHttpContextAccessor();

您现在可以HttpContext根据需要使用依赖注入进行解析。这可能有帮助,也可能没有帮助,具体取决于您对所使用的中间件的控制程度。

对于它的价值,我在没有额外第三方中间件的情况下仅使用 MSAL 对 AAD 进行身份验证几乎没有问题。祝你好运!


推荐阅读