.net core 将Azure AD与dotnet core 2 web api中的本地用户数据库相结合

.net core 将Azure AD与dotnet core 2 web api中的本地用户数据库相结合,.net-core,azure-active-directory,.net Core,Azure Active Directory,我正在创建一个.net-core2 web api,它允许来自Azure广告的用户使用它。API是多租户的,因此来自多个Azure AD的用户应该能够授权 但是,也可以为没有企业Azure广告帐户的用户创建帐户。这些用户存储在数据库(本地用户)中 因为它是一个web api,所以我实现了一个自定义令牌提供程序,以便本地用户可以获得令牌来使用受保护的web api 但是,我不能向web api添加两个单独的“承载者”身份验证: services.AddAuthentication(options

我正在创建一个.net-core2 web api,它允许来自Azure广告的用户使用它。API是多租户的,因此来自多个Azure AD的用户应该能够授权

但是,也可以为没有企业Azure广告帐户的用户创建帐户。这些用户存储在数据库(本地用户)中

因为它是一个web api,所以我实现了一个自定义令牌提供程序,以便本地用户可以获得令牌来使用受保护的web api

但是,我不能向web api添加两个单独的“承载者”身份验证:

services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options))
.AddJwtBearer(options => new JwtBearerOptions {
     TokenValidationParameters = tokenValidationParameters  
 });
这会引发一个错误:

System.InvalidOperationException:方案已存在:承载


我完全理解。但是如何并行实现这两种身份验证机制呢?

您必须指定不同的标识符。目前两者都在使用“承载者”标识符

例如,您可以通过以下方式为JWT承载指定不同的承载:

.AddJwtBearer("CustomJwt", options => { });
这解决了标识符冲突的问题,但是为了并行支持两个身份验证方案,您需要进行额外的修改

David Fowler提出了2.0中的一种方法:

在您的情况下,如果在点击中间件时上下文中没有用户,那么您可以使用所有承载(Azure AD)方案

在ASP.NET Core 2.1中,我们将获得“虚拟身份验证方案”,它以更一流的方式允许此方案:

多亏了朱纳斯,我找到了一个有效的解决办法。我所做的:

在Startup.cs ConfigureServices中,我添加了两种身份验证方案:

services.AddAuthentication(选项=>
{
options.DefaultScheme=JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(选项=>Configuration.Bind(“AzureAd”,选项))
.AddJwtBearer(“JWTBearer”,选项=>{
options.TokenValidationParameters=TokenValidationParameters;
});
然后确保在授权中启用两个方案:

services.AddAuthorization(配置=>{
config.AddPolicy(PolicyNames.RequireKeyUser,
策略=>
{
policy.AddRequirements(新的KeyUserRequirements());
policy.RequireAuthenticatedUser();
policy.addAuthenticationScheme(“JWTBearer”,JwtBearerDefaults.AuthenticationScheme);
});
});
并在配置中写入一些逻辑,以确定运行时的身份验证方案:

app.Use(异步(上下文,下一步)=>
{
//编写一些代码,根据传入的请求确定方案
var scheme=GetSchemeForRequest(上下文);
如果(!String.IsNullOrEmpty(scheme)){
var result=await context.authenticatesync(scheme);
if(result.successed)
{
context.User=result.Principal;
}
} 
等待下一个();
});
我决定使用一个额外的标题“Authorization Type”来定义我的自定义JWT授权,并在“Authorization”标题中使用默认的“Bearer”前缀。因此,我的GetSchemeForRequest函数:

私有字符串GetSchemeForRequest(HttpContext上下文)
{
var方案=”;
试一试{
if(!String.IsNullOrEmpty(context.Request.Headers[“Authorization”].ToString()){
字符串authHeader=context.Request.Headers[“授权类型”].ToString();
如果(authHeader==“JWT”){
scheme=“JWTBearer”;
}否则{
方案=“持票人”;
}
}
}
捕获(例外情况除外){
//使用您自己的日志记录机制
}
退货计划;
}

非常感谢。你帮了我正确的方向。有关详细信息,请参阅我的额外答案。
app.UseAuthentication();

app.Use(async (context, next) =>
{
    // Write some code that determines the scheme based on the incoming request
    var scheme = GetSchemeForRequest(context);
    var result = await context.AuthenticateAsync(scheme);
    if (result.Succeeded)
    {
        context.User = result.Principal;
    }
    await next();
});