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

时间:2018-10-23 15:20:22

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

我正在尝试使用Ocelot在ASP.Net API网关中与多个授权机构/发行者一起使用JWT承载身份验证。一个颁发者是Auth0,另一个是基于IdentityServer4的内部身份验证服务器。我们正在尝试从Auth0迁移过来,但仍然有外部客户端依赖它,因此我们希望同时支持这两种方法,直到对它们进行全面测试为止。

根据this MSDN blog post,应该可以通过设置TokenValidationParameters.ValidIssuers而不是JwtBearerOptions.Authority来使用多个权限。但是,无论TokenValidationParameters.ValidIssuers的内容如何,​​如果没有将颁发机构设置为颁发令牌的颁发机构,我都会在使用Ocelot和不使用Ocelot的情况下进行测试,并且不会进行身份验证。

有人知道如何使它工作吗?这就是我设置身份验证的方式。仅在注释行未注释的情况下才有效(并且仅适用于该单一机构颁发的令牌)。我期望Ocelot或ASP.Net Core可以从发行服务器获取密钥;两者都通过与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,还是我误解了它们的目的。

3 个答案:

答案 0 :(得分:2)

我想出了办法:

  1. 使用services.AddAuthentication()创建认证构建器。您可以根据需要将默认方案设置为“ Bearer”,但这不是必需的。

  2. 使用authenticationBuilder.AddJwtBearer()根据需要添加许多不同的JWT承载配置,每个配置都有其自己的密钥(例如“ Auth0”,“ IS4”等)。我在appsettings.json

  3. 中的数组上使用了循环
  4. 使用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支持此功能不需要任何特殊操作,只需使用“承载者”作为身份验证提供程序密钥,方案选择器策略将被自动调用。

答案 1 :(得分:0)

这是可行的示例:

public void ConfigureServices(IServiceCollection services)
{
   services.AddAuthentication(options => 
   {
       options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
   })
    //set default authentication 
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        //set the next authentication configuration to be used
        options.ForwardDefaultSelector = ctx => "idp4";

        //...rest of the options goes here
        };
    })
    .AddJwtBearer("idp4", options => 
     {
        //set the next authentication configuration to be used
        options.ForwardDefaultSelector = ctx => "okta";
        //options goes here
     })
    .AddJwtBearer("okta", options => 
     {
        //options goes here
     });

答案 2 :(得分:0)

.net 5 的有效解决方案。

  • 这适用于多个 JWT 不记名令牌发行者

  • 默认模式会路由到相应的模式

    page