我正在创建一个.net-core2 web-api,它允许Azure-AD中的用户使用它。 API是多租户的,因此来自多个Azure-AD的用户应该能够进行授权。
但是,也可以为没有企业Azure-AD帐户的用户创建帐户。这些用户存储在数据库(本地用户)中。
因为它是一个web-api,我实现了一个自定义令牌提供程序,以便本地用户可以获得一个令牌来使用受保护的web-api。
但是,我无法在web-api中添加两个单独的“Bearer”身份验证:
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options))
.AddJwtBearer(options => new JwtBearerOptions {
TokenValidationParameters = tokenValidationParameters
});
这会引发错误:
System.InvalidOperationException:Scheme已存在:Bearer
我完全理解。但是我如何能够并行实现两种身份验证机制呢?
答案 0 :(得分:1)
您必须指定其他标识符。两者都在使用" Bearer"目前的标识符。
例如,您可以通过以下方式为JWT Bearer指定另一个:
.AddJwtBearer("CustomJwt", options => { });
这解决了标识符冲突的问题,但为了并行支持两种身份验证方案,您需要进行其他修改。
2.0中的一种方式是David Fowler提出的建议:https://github.com/aspnet/Security/issues/1469
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();
});
在您的情况下,如果在您访问中间件时上下文中没有用户,则可以使用所有承载(Azure AD)方案。
在ASP.NET Core 2.1中,我们将获得"虚拟身份验证方案",它允许以更一流的方式使用此方案:https://github.com/aspnet/Security/pull/1550
答案 1 :(得分:0)
感谢juunas,我找到了一个有效的解决方案。我做了什么:
在Startup.cs ConfigureServices中,我添加了两种身份验证方案:
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options))
.AddJwtBearer("JWTBearer", options => {
options.TokenValidationParameters = tokenValidationParameters;
});
然后在授权中确保启用两个方案:
services.AddAuthorization(config => {
config.AddPolicy(PolicyNames.RequireKeyUser,
policy =>
{
policy.AddRequirements(new KeyUserRequirement());
policy.RequireAuthenticatedUser();
policy.AddAuthenticationSchemes("JWTBearer", JwtBearerDefaults.AuthenticationScheme);
});
});
在Configure中编写一些逻辑以确定运行时的auth方案:
app.Use(async (context, next) =>
{
// Write some code that determines the scheme based on the incoming request
var scheme = GetSchemeForRequest(context);
if (!String.IsNullOrEmpty(scheme)) {
var result = await context.AuthenticateAsync(scheme);
if (result.Succeeded)
{
context.User = result.Principal;
}
}
await next();
});
我决定使用额外的标题'Authorization-Type'来定义我的自定义JWT授权,并在'Authorization'标题中使用默认的'Bearer'前缀。所以我的GetSchemeForRequest函数:
private string GetSchemeForRequest(HttpContext context)
{
var scheme = "";
try {
if (!String.IsNullOrEmpty(context.Request.Headers["Authorization"].ToString())) {
string authHeader = context.Request.Headers["Authorization-Type"].ToString();
if (authHeader == "JWT") {
scheme = "JWTBearer";
} else {
scheme = "Bearer";
}
}
}
catch (Exception ex) {
// Use your own logging mechanism
}
return scheme;
}