我有一个 ASP.NET Core 3.1 Web API 项目,没有强制执行安全性。现在我需要使用基于 API 密钥的身份验证方案来保护它。
基本思想是让此 Web API 的客户端在标头中传入 API 密钥,然后所有客户端 API 密钥将保存在
appsettings.json
文件中。
这些是实施细节:
ApiKeyAuthenticationOptions
,其中 MyConfigValue
将用于按住按键 public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
public const string DefaultScheme = "ApiKey";
public string Scheme => DefaultScheme;
public string AuthenticationType = DefaultScheme;
public string MyConfigValue { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddApiKeyAuthentication(Configuration);
services.AddControllers();
}
AuthenticationBuilder
的扩展方法定义为 public static AuthenticationBuilder AddApiKeyAuthentication(this AuthenticationBuilder builder, IConfiguration configuration)
{
var config = configuration["MyConfigValue"];
builder.Services.Configure<ApiKeyAuthenticationOptions>(options =>
{
options.MyConfigValue = config;
});
builder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationOptions.DefaultScheme, options => { });
return builder;
}
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
private const string ApiKeyName = "X-Api-Key";
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var myConfigValue = Options.MyConfigValue;
if (!Request.Headers.ContainsKey(ApiKeyName))
{
return AuthenticateResult.Fail("Missing API Key");
}
var apiKey = Request.Headers[ApiKeyName];
if (apiKey != myConfigValue)
{
return AuthenticateResult.Fail("Invalid API Key");
}
var claims = new[] { new Claim(ClaimTypes.NameIdentifier, "api-user") };
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
问题出现在这里:
options
中的constructor
有一个属性CurrentValue
,该属性具有自定义属性MyConfigValue
,具有来自appsettings
的正确值,但是,在函数HandleAuthenticateAsync
中,Options
没有属性 CurrentValue
,但它具有自定义属性 MyConfigValue
,该属性为 null。因此,构造函数中的options
与函数中的Options
不一样。我对 ApiKeyAuthenticationHandler
类的实现可能是错误的。现在的一种解决方法是检索 MyConfigValue
内的 constructor
并将其保存以供在函数中使用。
使用以下方式保护 API 调用:
[Authorize(AuthenticationSchemes = "ApiKey")]
我将
MyConfigValue
设置在appsettings.json
的根级别。HandleAuthenticateAsync
时,options.MyConfigValue
始终为 null。ApiKeyAuthenticationOptions
而不包含MyConfigValue
,然后在ApiKeyAuthenticationHandler
中获取它,那么它就可以工作。但这需要为每个 API 调用获取 MyConfigValue
,效率不高。有人可以告诉我出了什么问题吗?
提前非常感谢。
builder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationOptions.DefaultScheme, options => { });
重置导致您出现问题的ApiKeyAuthenticationOptions。
您应该直接使用
builder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationOptions.DefaultScheme, options => { options.MyConfigValue = configuration["MyConfigValue"]; });
详情如下:
public static class AuthenticationBuilderExtensions
{
public static AuthenticationBuilder AddApiKeyAuthentication(this AuthenticationBuilder builder, IConfiguration configuration)
{
var config = configuration["MyConfigValue"];
builder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationOptions.DefaultScheme, options => { options.MyConfigValue = configuration["MyConfigValue"]; });
return builder;
}
}