C# 在中间件之前调用自定义AuthenticationHandler

C# 在中间件之前调用自定义AuthenticationHandler,c#,asp.net-core,.net-core,C#,Asp.net Core,.net Core,我有一个ASP.NET核心WebAPI(2.2),它使用两种身份验证类型: JWT承载者 APIKey(自定义) 这是我在Startup.cs中配置它的方式: services.AddAuthentication(sharedOptions=> { sharedOptions.DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme; sharedOptions.DefaultChallengeScheme=JwtBea

我有一个ASP.NET核心WebAPI(2.2),它使用两种身份验证类型:

  • JWT承载者
  • APIKey(自定义)
这是我在Startup.cs中配置它的方式:

services.AddAuthentication(sharedOptions=>
{
sharedOptions.DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme=JwtBearerDefaults.AuthenticationScheme;
})
.AddApiKeyAuthentication(options=>Configuration.Bind(“ApiKeyAuth”,options));
services.AddAuthentication(选项=>
{
options.DefaultScheme=JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions=>
{
jwtOptions.Authority=$”https://login.microsoftonline.com/tfp/{Configuration[“AzureAdB2C:Tenant”]}/{Configuration[“AzureAdB2C:Policy”]}/v2.0/”;
jwtOptions.accession=Configuration[“AzureAdB2C:ClientId”];
事件=新的JWTBeareEvents
{
OnAuthenticationFailed=身份验证失败
};
});
我的控制器由一个
Authorize
属性修饰,该属性包含两个方案。因此,我可以使用承载令牌或通过在报头中指定API密钥来调用我的web方法,这很好

现在,我添加了一个自定义中间件,在其中执行一些特定于租户的检查。我在
Configure
方法中注册了中间件(在
UseAuthentication
之后):

/。。。。
app.UseAuthentication();
app.UseMiddleware()
现在,如果我使用bearer身份验证调用REST方法,我的
CustomMiddleware
将被一个经过身份验证的用户调用,我可以访问声明

如果使用自定义APIKey身份验证调用相同的REST方法,则在my
AuthenticationHandler.handleAuthenticationAsync()方法之前调用my
CustomMiddleware
。用户未通过身份验证-我无法访问声明(我在
handleAuthenticateAncy
方法中填充了声明)


为什么在身份验证之前调用中间件?如何更改该行为,以便在
handleAuthenticationAsync()
之后调用中间件

您需要设置一个前向默认选择器,以便在必要时将API密钥身份验证方案设为默认方案

以下是一个例子:

services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions =>
{
    jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["AzureAdB2C:Tenant"]}/{Configuration["AzureAdB2C:Policy"]}/v2.0/";
    jwtOptions.Audience = Configuration["AzureAdB2C:ClientId"];
    jwtOptions.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = AuthenticationFailed
    };
    jwtOptions.ForwardDefaultSelector = ctx =>
    {
        if (ctx.Request.Headers.TryGetValue("Api-Key", out var headerValues))
        {
            return "ApiKeyAuth";
        }

        return null;
    };
})
.AddApiKeyAuthentication(options => Configuration.Bind("ApiKeyAuth", options));
我做了一些猜测来构造选择器, 但如果使用另一个方案,基本上需要返回另一个方案的名称, 否则无效

在本例中,它检查请求中是否有“Api密钥”头, 如果是,则返回“ApiKeyAuth”,这是API密钥方案的名称。 如果这些值不同,则将其替换为您的值

如果在两个方案中都使用了Authorization:Bearer xyz头,
您需要检查选择器中的标题内容才能做出决定。

JWTBearner是否配置为默认身份验证方案?您的代码片段没有显示您是如何设置默认方案的。@KirkLarkin感谢您的帮助。我添加了我的精确配置。对于所讨论的REST方法,它是一个具有
[Authorize]
属性的MVC操作。如果不是,那是什么?我意识到会有不止一种受影响的方法,但使用特定的示例更容易。我的所有控制器都使用从
AuthorizeAttribute
继承的CustomeAuthorize属性,并将
AuthenticationSchemes
设置为
AuthenticationSchemes=“ApiKey,Bearer”
services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions =>
{
    jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["AzureAdB2C:Tenant"]}/{Configuration["AzureAdB2C:Policy"]}/v2.0/";
    jwtOptions.Audience = Configuration["AzureAdB2C:ClientId"];
    jwtOptions.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = AuthenticationFailed
    };
    jwtOptions.ForwardDefaultSelector = ctx =>
    {
        if (ctx.Request.Headers.TryGetValue("Api-Key", out var headerValues))
        {
            return "ApiKeyAuth";
        }

        return null;
    };
})
.AddApiKeyAuthentication(options => Configuration.Bind("ApiKeyAuth", options));