基于API Key的ASP.NET Core认证中自定义选项无法生效
问题
我有一个未启用安全机制的ASP.NET Core 3.1 Web API项目,需要通过基于API Key的认证方案保护它。核心逻辑是让客户端在请求头传入API Key,合法的API Key存储在appsettings.json中。
已完成的实现步骤如下:
- 自定义
ApiKeyAuthenticationOptions类存储API Key:
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions { public const string DefaultScheme = "ApiKey"; public string Scheme => DefaultScheme; public string AuthenticationType = DefaultScheme; public string MyConfigValue { get; set; } }
- 在
Startup的ConfigureServices中添加认证逻辑:
public void ConfigureServices(IServiceCollection services) { services.AddSingleton(Configuration); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddApiKeyAuthentication(Configuration); services.AddControllers(); }
- 定义
AuthenticationBuilder的扩展方法AddApiKeyAuthentication:
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; }
- 实现认证处理器
ApiKeyAuthenticationHandler:
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参数CurrentValue能正确读取到appsettings.json中的MyConfigValue,但HandleAuthenticateAsync方法里的Options.MyConfigValue始终为null。 - 已用
[Authorize(AuthenticationSchemes = "ApiKey")]标记保护接口,appsettings.json根节点也配置了MyConfigValue,但认证时Options.MyConfigValue仍为null。 - 若直接在处理器中读取配置可正常工作,但每次请求都读配置效率较低,希望找到问题根源并解决。
原因分析与解决方案
问题根源
你在调用AddScheme时传入了空的配置委托options => { },这会覆盖之前通过Configure<ApiKeyAuthenticationOptions>设置的全局配置。
在ASP.NET Core认证系统中,AddScheme方法的最后一个参数是用于配置当前Scheme专属选项的委托。当你传入空委托时,系统会创建一个全新的ApiKeyAuthenticationOptions实例(所有属性取默认值,比如MyConfigValue为null),并覆盖之前通过Configure注册的配置。
修复方法
修改AddApiKeyAuthentication扩展方法,移除空配置委托即可:
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); return builder; }
额外优化建议
ApiKeyAuthenticationOptions继承自AuthenticationSchemeOptions,无需自行定义Scheme和AuthenticationType属性,基类会自动处理Scheme名称。- 建议将API Key配置放在专门的节点中,符合ASP.NET Core配置规范:
- 在
appsettings.json中添加:"ApiKeySettings": { "ValidApiKey": "your-actual-api-key" } - 创建对应配置类:
public class ApiKeySettings { public string ValidApiKey { get; set; } } - 在
ConfigureServices中绑定配置:services.Configure<ApiKeySettings>(Configuration.GetSection("ApiKeySettings"));
- 在
内容的提问来源于stack exchange,提问作者user3901397
相关产品推荐
相关产品推荐

