ASP.NET Core MVC v2.0自定义认证Handler调用HandleAuthenticateAsync时Options为空
IOptionsMonitor<MyOptions>为空的问题 我之前在自定义认证方案时也踩过类似的配置坑,结合你的代码场景,咱们从这几个核心方向排查解决:
1. 优先使用认证基类自带的Options属性
你的自定义认证处理程序应该是继承自AuthenticationHandler<MyOptions>吧?这个基类已经通过构造函数自动注入了IOptionsMonitor<MyOptions>,并通过Options属性暴露了当前scheme对应的配置实例。
如果你的处理程序是手动额外注入IOptionsMonitor<MyOptions>,很可能拿到的是全局默认配置,而非你在AddSingleSignOn中针对该scheme单独配置的实例。建议直接用基类的属性:
public class SingleSignOnHandler : AuthenticationHandler<MyOptions> { public SingleSignOnHandler(IOptionsMonitor<MyOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { // 直接用base.Options,而非自己注入的实例 var globalDb = Options.OpenGlobalDb(); // ...后续逻辑 } }
2. 检查AddSingleSignOn扩展方法的实现
你的扩展方法必须正确关联认证scheme、MyOptions和处理程序,否则配置无法绑定到对应的实例。正确的扩展方法应该是这样的:
public static AuthenticationBuilder AddSingleSignOn(this AuthenticationBuilder builder, string authenticationScheme, Action<MyOptions> configureOptions) { // 关键:调用AddScheme绑定scheme、Options和处理程序的关联 return builder.AddScheme<MyOptions, SingleSignOnHandler>(authenticationScheme, configureOptions); }
如果扩展方法里没有做这一步绑定,IOptionsMonitor就无法找到你在Startup中配置的那个Options实例,自然会返回空。
3. 确认认证中间件的执行顺序与scheme匹配
在Startup的Configure方法中,要保证UseAuthentication放在UseMvc之前,同时确保授权时指定的scheme和你配置的完全一致(大小写敏感):
// 先启用认证中间件 app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
比如控制器上的授权特性要写:[Authorize(AuthenticationSchemes = "SingleSignOn")],和你AddSingleSignOn中传入的scheme名称完全对应。
4. 验证MyOptions类的定义
确保MyOptions是公共类,且属性为可读写状态,最好继承ASP.NET Core认证体系的标准基类AuthenticationSchemeOptions,避免兼容性问题:
public class MyOptions : AuthenticationSchemeOptions { public Func<DbConnection> OpenGlobalDb { get; set; } public Func<string, DbConnection> OpenTenantDb { get; set; } }
快速排查技巧
如果还是为空,先在HandleAuthenticateAsync里打印基类Options的哈希值,再在Startup的AddSingleSignOn配置委托里打印options的哈希值,对比两者是否一致——如果不一致,说明配置没有正确关联到处理程序,重点检查扩展方法的AddScheme调用。
内容的提问来源于stack exchange,提问作者jgauffin

