我在ASP.NET核心API项目中遇到了一个奇怪的情况.我正在试着通过IActionContextAccessor IS AuthenticationHandler进入ActionContext.现在我发现,当我只有1AuthenticationScheme个注册时,返回的ActionContextnull,但是一旦我添加第二个AuthenticationScheme,ActionContext就正确地返回了.

下面的代码是最小化的再现场景.我有一个设置身份验证方案的程序.cs.当我在项目中调用一个随机的API路径,并且在HandleAuthenticateAsync方法中有一个断点时,我看到ActionContext被填满了.然而,一旦我注释掉注册Schema2身份验证处理程序的行,ActionContext就变成空的.当命中同一断点时.

有人能解释一下这里发生了什么吗?

我的程序.cs:

using AuthHandlerTest;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Infrastructure;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
builder.Services.AddControllers(options =>
{
    options.Filters.Add(new AuthorizeFilter());
});
builder.Services.AddAuthentication()
    .AddScheme<Schema1Options, Schema1>("schema1", null);
    .AddScheme<Schema2Options, Schema2>("schema2", null);
builder.Services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder("schema1").RequireAuthenticatedUser().Build();
});

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthentication();
app.UseRouting();

app.MapControllers();

app.Run();

我的架构1.cs

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using System.Text.Encodings.Web;

namespace AuthHandlerTest;

public class Schema1Options : AuthenticationSchemeOptions
{
}

public class Schema1 : AuthenticationHandler<Schema1Options>
{
    private readonly IActionContextAccessor _actionContextAccessor;

    public Schema1(IOptionsMonitor<Schema1Options> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IActionContextAccessor actionContextAccessor) : base(options, logger, encoder, clock)
    {
        _actionContextAccessor = actionContextAccessor;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        //this variable is null when only having 1 scheme registered
        var actionContext = _actionContextAccessor.ActionContext;

        var claims = new[]
        {
                new Claim(ClaimTypes.NameIdentifier, "aa"),
            };

        var claimsIdentity = new ClaimsIdentity(claims, "schema1");
        var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), "schema1");
        return Task.FromResult(AuthenticateResult.Success(ticket));
    }
}

架构2.cs与架构1.cs相同,只是1‘S被替换为2’S.

我还看到,在工作和非工作场景中,堆栈跟踪有很大的不同.

Working: Stacktrace of working situation

Non working: Stacktrace of non-working situation

现在我理解了为什么这些不同的堆栈导致ActionContext可用或不可用,因为在工作情况下,ResourceInvoker存在于调用堆栈中,并且该组件确保IActionContextAccessor获得ActionContext属性.重要的问题是,是什么导致了调用堆栈的差异

最新情况: 我添加了一个完整的重现场景here

推荐答案

这是一张related issue美元的

IActionContextAccessor.ActionContext在MVC作用域之外为空 中间件

部分源代码Authentication middleware

var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
        if (defaultAuthenticate != null)
        {
            var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
            if (result?.Principal != null)
            {
                context.User = result.Principal;
            }
            if (result?.Succeeded ?? false)
            {
                var authFeatures = new AuthenticationFeatures(result);
                context.Features.Set<IHttpAuthenticationFeature>(authFeatures);
                context.Features.Set<IAuthenticateResultFeature>(authFeatures);
            }
        }

        await _next(context);

如果默认方案不为空,则context.AuthenticateAsync(defaultAuthenticate.Name);将在身份验证中间件中执行(如堆栈跟踪所示),这意味着处理程序将被调用到MVC中间件的范围之外

与这个问题有关的document

从ASP.NET Core 7.0开始,如果(且仅当)单个方案 在应用程序中注册的,该方案被视为默认方案. 在下面的代码中,CookieDefaults.AuthenticationSolutions是 被视为默认方案.

但是,在下一个代码片段中,没有设置默认值,因为多个 计划是注册的.

要禁用它,请执行以下操作:

AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", true);

它现在在我这边起作用了:

enter image description here

enter image description here

Csharp相关问答推荐

C#中的两个列表中的元素加/减/乘/除操作

EF Core的GbContent如何 suppress 所有CS 8618(不可为空属性)警告?

我如何才能获得被嘲笑班级的私有成员?

VB.Net的SON模式导致集合代码不工作

获取Windows和Linux上的下载文件夹

需要更改哪些内容才能修复被覆盖的财产中的无效警告CS 8765?

Polly使用泛型重试和重试包装函数

限制特定REST API不被访问,但部署代码

Azure DEVOPS找不到定制的Nuget包

在不添加不必要的尾随零的情况下本地化浮点型?

VS 2022 for ASP.NET Core中缺少自定义项模板

自定义列表按字符串的部分排序

如何在.NET AOT中为所有枚举启用JsonStringEnumConverter

使用动态键从请求体反序列化JSON

当我try 在与XAMP的MySQL服务器连接的ASP.NET核心应用程序中添加迁移时出现错误

Blazor:搜索框在第一次搜索时不搜索

使用可空引用类型时C#接口实现错误

当我手动停止和关闭系统并打开时,Windows服务未启动

使用DI实例化带有动态参数的服务?

读取测试项目中的应用程序设置