.NET Core Web API 密钥

26

我正在开发一个应用程序,用户可以通过用户名和密码进行身份验证,我们提供JWT令牌,然后在服务器上进行验证。

我想添加的一个功能是,允许用户在集成此应用程序时使用特殊的API密钥(GUID),而不是使用用户名和密码进行身份验证。

由于身份验证部分似乎有点黑盒子(使用Aspnet Identity),我不确定该如何实现这一点。

这里是我身份验证设置的一些代码。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<OmbiContext>(options =>
        options.UseSqlite("Data Source=Ombi.db"));

    services.AddIdentity<OmbiUser, IdentityRole>()
        .AddEntityFrameworkStores<OmbiContext>()
        .AddDefaultTokenProviders();

    services.Configure<IdentityOptions>(options =>
    {
        options.Password.RequireDigit = false;
        options.Password.RequiredLength = 1;
        options.Password.RequireLowercase = false;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = false;
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IMemoryCache cache)
{
    var tokenOptions = (IOptions<TokenAuthentication>)app.ApplicationServices.GetService(
        typeof(IOptions<TokenAuthentication>));

    var ctx = (IOmbiContext)app.ApplicationServices.GetService(typeof(IOmbiContext));

    var tokenValidationParameters = new TokenValidationParameters
    {

        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOptions.Value.SecretKey)),

        RequireExpirationTime = true,
        ValidateLifetime = true,
        ValidAudience = "Ombi",
        ValidIssuer = "Ombi",
        ClockSkew = TimeSpan.Zero
    };

    app.UseJwtBearerAuthentication(new JwtBearerOptions()
    {
        Audience = "Ombi",
        AutomaticAuthenticate = true,
        TokenValidationParameters =  tokenValidationParameters,

    });
 //....
}

当控制器上有[Authorized]属性并检查角色等信息时,上述代码可以正常工作。

有人知道我如何在所有请求中传递某种Api-Key头,包含此特殊API密钥以使其通过[Authorized]属性?(密钥存储在数据库中。)


https://dev59.com/CVwZ5IYBdhLWcg3wbv7r - stuartd
@stuartd 不确定上述内容是否适用,看起来我需要为每个控制器定义该策略,在这种情况下,API密钥标头将始终需要存在。基本上,我正在寻找一种向服务器提供授权我的秘密的方法。 - Jamie Rees
您需要在请求中使用Authorization头提供Bearer Token。根据授权服务器的位置,您需要对其进行验证。例如,在Azure AD中,如果您只想针对服务器中的硬编码密钥进行验证,则可以通过自己的声明验证器进行验证,检查AuthorizationHandler类。 - joakimja
2个回答

28

这是最终我所做的:

 public static void ApiKeyMiddlewear(this IApplicationBuilder app, IServiceProvider serviceProvider)
    {
        app.Use(async (context, next) =>
        {
            if (context.Request.Path.StartsWithSegments(new PathString("/api")))
            {
                // Let's check if this is an API Call
                if (context.Request.Headers["ApiKey"].Any())
                {
                    // validate the supplied API key
                    // Validate it
                    var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
                    await ValidateApiKey(serviceProvider, context, next, headerKey);
                }
                else if (context.Request.Query.ContainsKey("apikey"))
                {
                    if (context.Request.Query.TryGetValue("apikey", out var queryKey))
                    {
                        await ValidateApiKey(serviceProvider, context, next, queryKey);
                    }
                }
                else
                {
                    await next();
                }
            }
            else
            {
                await next();
            }
        });
    }

    private static async Task ValidateApiKey(IServiceProvider serviceProvider, HttpContext context, Func<Task> next, string key)
    {
        // validate it here
        var valid = false;
        if (!valid)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            await context.Response.WriteAsync("Invalid API Key");
        }
        else
        {
            var identity = new GenericIdentity("API");
            var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
            context.User = principal;
            await next();
        }
    }
这个问题自问答以来发生了相当大的变化(答案仍然有效)。但是你可以在这里阅读有关此事的内容:https://tidusjar.github.io/2019/03/25/net-core-jwt-api-key/

1
你能详细说明一下在 ValidateApiKey 的 else 体中你想要实现什么吗?我的担心是对 ApiKey 进行验证,但在带有 [Authorize] 属性的操作上被拒绝。您如何处理中间件以跳过该属性? - Guido Dizioli
基本上 await context.Response.WriteAsync("Invalid API Key");,然后 await next.Invoke(context);. 您可以在这里查看:https://github.com/tidusjar/Ombi/blob/master/src/Ombi/ApiKeyMiddlewear.cs#L92 - Jamie Rees
嘿@JamieRees,谢谢你提供的解决方案,它对我很有帮助! 您认为我们可以不检查ApiKey,而是查找client_id并检查该client_id是否具有对受保护范围(如Api资源)的访问权限? - rafaelvascc

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接