C# 我如何拥有多个身份验证方法,而只有一个方法必须成功?

C# 我如何拥有多个身份验证方法,而只有一个方法必须成功?,c#,asp.net-core,C#,Asp.net Core,不幸的是,在这件事上,我找不到任何对我有帮助的东西 通常API使用OIDC来处理身份验证,因此我们将普通.AddAuthentication.AddJwtBearer用作默认值。这很好,但我们还需要能够有另一种身份验证方式来提供用户身份,因为令牌并不总是可用的。假设它是一个ApiKey,其中包含给定ApiKey的用户字典 尽管如此,我还是希望JWT令牌优先于另一层,因为它只是一种更强的身份验证。所以它应该是这样的: 我们收到请求 若我们看到授权头,那个么这意味着请求提供了令牌——我们想要验证那个

不幸的是,在这件事上,我找不到任何对我有帮助的东西

通常API使用OIDC来处理身份验证,因此我们将普通
.AddAuthentication.AddJwtBearer
用作默认值。这很好,但我们还需要能够有另一种身份验证方式来提供用户身份,因为令牌并不总是可用的。假设它是一个ApiKey,其中包含给定ApiKey的用户字典

尽管如此,我还是希望JWT令牌优先于另一层,因为它只是一种更强的身份验证。所以它应该是这样的:

  • 我们收到请求
  • 若我们看到授权头,那个么这意味着请求提供了令牌——我们想要验证那个令牌,若它无效——我们想要401,因为有些地方不正确
  • 若HttpRequest中并没有授权头,但我们有ApiKey头—我们希望完全忽略验证令牌,并尝试检查该ApiKey。如果它是有效的,我们希望从那里设置HttpContext.User,并告知整个身份验证顺利进行,我们的控制器可以正常使用HttpContext.User
  • 目前,我们有多个控制器端点支持令牌或ApiKey(不同属性)场景,但当控制器和端点的数量增加时,复制所有内容可能会非常痛苦

    我尝试附加到一些
    JwtBearerEvents
    并检查其中的标题,以便在发现没有可用令牌时手动完成身份验证,例如:

    OnMessageReceived = async context =>
    {
        if (!context.Request.Headers.ContainsKey("Authorization") 
            && context.Request.Headers.ContainsKey("ApiKey"))
        {
             // some logic to check that ApiKey
             context.Principal = new ClaimsPrincipal(new ClaimsIdentity(
                 new Claim[]
                 {
                     new Claim(ClaimTypes.NameIdentifier, "ApiKeyUser")
                 }));
             context.Success();
        }
        await Task.CompletedTask;
    }
    
    services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = smartAuthenticationScheme; // just some string
                options.DefaultChallengeScheme = smartAuthenticationScheme;
                options.DefaultScheme = smartAuthenticationScheme;
            })
            .AddPolicyScheme(smartAuthenticationScheme, "Token / Custom", options =>
            {
                options.ForwardDefaultSelector = context =>
                {
                    var header = (string)context.Request.Headers["Authorization"];
    
                    if (header == null || string.IsNullOrEmpty(header))
                        return undefinedAuthenticationScheme;
    
                    if (header.StartsWith("Bearer"))
                        return bearerAuthenticationScheme; // it's just JwtBearerDefaults.AuthenticationScheme
                    if (header.StartsWith("Custom"))
                        return customAuthenticationScheme;
    
                    return undefinedAuthenticationScheme;
                };
            })
            .AddJwt(bearerAuthenticationScheme, configuration)
            .AddCustom(customAuthenticationScheme)
            .AddUndefined(undefinedAuthenticationScheme);
    

    不幸的是,中间件仍然坚持检查令牌并返回401,所以这很可能不是解决该问题的有效方法。

    我找到了一种方法,所以我将把它留在这里。您只需使用
    AddPolicyScheme
    添加选择器,该选择器将通过按名称调用来使用不同的处理程序

    例如:

    OnMessageReceived = async context =>
    {
        if (!context.Request.Headers.ContainsKey("Authorization") 
            && context.Request.Headers.ContainsKey("ApiKey"))
        {
             // some logic to check that ApiKey
             context.Principal = new ClaimsPrincipal(new ClaimsIdentity(
                 new Claim[]
                 {
                     new Claim(ClaimTypes.NameIdentifier, "ApiKeyUser")
                 }));
             context.Success();
        }
        await Task.CompletedTask;
    }
    
    services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = smartAuthenticationScheme; // just some string
                options.DefaultChallengeScheme = smartAuthenticationScheme;
                options.DefaultScheme = smartAuthenticationScheme;
            })
            .AddPolicyScheme(smartAuthenticationScheme, "Token / Custom", options =>
            {
                options.ForwardDefaultSelector = context =>
                {
                    var header = (string)context.Request.Headers["Authorization"];
    
                    if (header == null || string.IsNullOrEmpty(header))
                        return undefinedAuthenticationScheme;
    
                    if (header.StartsWith("Bearer"))
                        return bearerAuthenticationScheme; // it's just JwtBearerDefaults.AuthenticationScheme
                    if (header.StartsWith("Custom"))
                        return customAuthenticationScheme;
    
                    return undefinedAuthenticationScheme;
                };
            })
            .AddJwt(bearerAuthenticationScheme, configuration)
            .AddCustom(customAuthenticationScheme)
            .AddUndefined(undefinedAuthenticationScheme);
    
    这样,我们的中间件将查看授权头的内容,并调用在给定名称下注册的AuthenticationHandler。当授权头的内容既不匹配Jwt也不匹配自定义AuthorizationHandler时,UndefinedHandler仅为401响应提供服务