asp.net-core - Asp.Net Core中的多个JWT授权机构/发布者

标签 asp.net-core jwt identityserver4 bearer-token ocelot

我正在尝试使用Ocelot在ASP.Net API网关中使用JWT承载身份验证,以与多个授权机构/发行者一起工作。一个颁发者是Auth0,另一个是基于IdentityServer4的内部身份验证服务器。我们正在尝试从Auth0迁移过来,但是仍然有外部客户端依赖它,因此我们希望同时支持这两种方法,直到对它们进行全面测试为止。
根据this MSDN blog post的介绍,应该可以通过设置TokenValidationParameters.ValidIssuers而不是JwtBearerOptions.Authority来使用多个权限。但是,无论是否有Ocelot,我都对此进行了测试,并且无论TokenValidationParameters.ValidIssuers的内容如何,​​如果未将颁发机构设置为颁发 token 的颁发机构,都不会进行身份验证。
有人知道如何使它工作吗?这就是我设置身份验证的方式。仅在注释行未注释的情况下才有效(并且仅适用于该单一机构颁发的 token )。我期望Ocelot或ASP.Net Core可以从发行服务器获取 key ;两者都通过与ASP.Net Core中间件一起使用的。众所周知/openid-configuration提供JWK。

public static void AddJwtBearerAuthentication(this IServiceCollection services, IConfiguration configuration)
{
    services
        .AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
        {
            //options.Authority = configuration["Jwt:Authority"];
            options.Audience  = configuration["Jwt:Audience"];
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer           = true,
                ValidateIssuerSigningKey = true,
                ValidateAudience         = true,
                ValidAudience            = configuration["Jwt:Audience"],
                ValidIssuers             = configuration
                    .GetSection("Jwt:Authorities")
                    .AsEnumerable()
                    .Select(kv => kv.Value)
                    .Where(s => !string.IsNullOrEmpty(s))
                    .ToArray()
            };
        });
}
当发出错误的发行人的客户端(或当我们使用TokenValidationParameters.ValidIssuer/ValidIssuers的客户端)连接时,Ocelot的输出为:
[16:35:37 WRN] requestId: _____, previousRequestId: no previous request id, message: Error Code: UnauthenticatedError Message: Request for authenticated route _____ by  was unauthenticated errors found in ResponderMiddleware. Setting error response for request path:_____, request method: POST
这是一个client_credentials身份验证,因此在“by”之后缺少用户名。如您所见,Ocelot并未说出确切的问题是什么。 ASP.Net Core JWT承载中间件(不带Ocelot)仅表示签名无效。我怀疑它不是在查看TokenValidationParameters,还是我误解了它们的目的。

最佳答案

我想出了怎么做:

  • 使用services.AddAuthentication()创建认证构建器。您可以根据需要设置默认方案(“Bearer”),但这不是必需的。
  • 使用authenticationBuilder.AddJwtBearer()根据需要添加许多不同的JWT承载配置,每个配置都有其自己的 key (例如“Auth0”,“IS4”等)。我在appsettings.json中的数组上使用了循环
  • 使用authenticationBuilder.AddPolicyScheme创建一个策略方案,并为其指定方案名称“Bearer”(使用JwtBearerDefaults.AuthenticationScheme避免代码中包含魔术字符串),然后在回调函数中将options.ForwardDefaultSelector设置为一个函数,该函数会返回其他方案名称之一(“Auth0”, “IS4”或您输入的任何内容),具体取决于某些标准。在我的情况下,它只是在JWT颁发者中查找方案名称(如果颁发者包含“auth0”,则使用Auth0方案)。

  • 代码:
    public static void AddMultiSchemeJwtBearerAuthentication(
        this IServiceCollection services,
        IConfiguration configuration
    )
    {
        // Create JWT Bearer schemes.
        var schemes = configuration
            .GetSection("Jwt")
            .GetChildren()
            .Select(s => s.Key)
            .ToList()
        ;
        var authenticationBuilder = services.AddAuthentication();
        foreach (var scheme in schemes)
        {
            authenticationBuilder.AddJwtBearer(scheme, options =>
            {
                options.Audience  = configuration[$"Jwt:{scheme}:Audience"];
                options.Authority = configuration[$"Jwt:{scheme}:Authority"];
            });
        }
    
        // Add scheme selector.
        authenticationBuilder.AddPolicyScheme(
            JwtBearerDefaults.AuthenticationScheme,
            "Selector",
            options =>
            {
                options.ForwardDefaultSelector = context =>
                {
                    // Find the first authentication header with a JWT Bearer token whose issuer
                    // contains one of the scheme names and return the found scheme name.
                    var authHeaderNames = new[] {
                        HeaderNames.Authorization,
                        HeaderNames.WWWAuthenticate
                    };
                    StringValues headers;
                    foreach (var headerName in authHeaderNames)
                    {
                        if (context.Request.Headers.TryGetValue(headerName, out headers) && !StringValues.IsNullOrEmpty(headers))
                        {
                            break;
                        }
                    }
    
                    if (StringValues.IsNullOrEmpty(headers))
                    {
                        // Handle error. You can set context.Response.StatusCode and write a
                        // response body. Returning null invokes default scheme which will raise
                        // an exception; not sure how to fix this so the request is rejected.
                        return null;
                    }
    
                    foreach (var header in headers)
                    {
                        var encodedToken = header.Substring(JwtBearerDefaults.AuthenticationScheme.Length + 1);
                        var jwtHandler = new JwtSecurityTokenHandler();
                        var decodedToken = jwtHandler.ReadJwtToken(encodedToken);
                        var issuer = decodedToken?.Issuer?.ToLower();
                        foreach (var scheme in schemes)
                        {
                            if (issuer?.Contains(scheme.ToLower()) == true)
                            {
                                // Found the scheme.
                                return scheme;
                            }
                        }
                    }
                    // Handle error.
                    return null;
                };
            }
        );
    }
    
    Ocelot不需要任何特殊支持即可,只需使用“Bearer”作为身份验证提供程序 key ,方案选择器策略将被自动调用。

    关于asp.net-core - Asp.Net Core中的多个JWT授权机构/发布者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52952600/

    相关文章:

    c# - 将文件上传到 Azure 上托管的 ASP.NET Core Web 应用程序的目录

    c# - 在 service .net core 中获取 appsettings.json 值

    asp.net-core - 您如何使用 .net Core 进行离线开发设​​置

    c# - 在 C# 中手动验证 JWT token

    asp.net-core - 如何在 ASP.Net Core 2 Web 应用程序上获取 CurrentPrincipal 身份?

    session - JWT 和签名 cookie 有什么区别?

    javascript - 类型错误 : JwtStrategy is not a constructor; NodeJS

    javascript - 如何在浏览器中存储 JWT?

    asp.net - IdentityServer混合流-用户成功登录后访问 token 为空

    identityserver4 - IdentitySever4 用户声明和 ASP.NET User.Identity