如何才能拥有多种身份验证方法而只有一种方法必须成功?

Kha*_*ine 3 c# asp.net-core

不幸的是我找不到任何可以帮助我解决这个问题的东西。

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

尽管如此,我还是希望 JWT 令牌比另一层具有优先级,因为它只是更强的身份验证。所以它会是这样的:

  1. 我们收到请求
  2. 如果我们看到授权标头,则意味着请求提供了令牌 - 我们想要验证该令牌(如果它无效) - 我们需要 401,因为有些事情不对劲。
  3. 如果 HttpRequest 中没有 Authorization 标头,但我们有 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;
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,中间件无论如何都坚持检查令牌并返回 401,因此这很可能不是解决该问题的有效方法。

Kha*_*ine 5

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

例如:

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);
Run Code Online (Sandbox Code Playgroud)

这样我们的中间件将查看 Authorization 标头的内容并调用以给定名称注册的 AuthenticationHandler 。当 Authorization header 的内容既不匹配 Jwt 也不匹配 Custom AuthorizationHandler 时,UndefinedHandler 的存在只是为了提供 401 响应。