ASP.NET Core 中失败的授权要求的错误状态代码

Wrong status code for failed autorization requirement in ASP.NET Core

我通过自动化要求创建了具有简单自动化的网络 api。我的需求代码如下:

public class TestRequirement : IAuthorizationRequirement { }

public class TestHandler : AuthorizationHandler<TestRequirement> {
    protected override Task
        HandleRequirementAsync(AuthorizationHandlerContext context, TestRequirement requirement) {
        //context.Succeed(requirement); --#1
        //context.Fail(); --#2
        /*if (context.Resource is AuthorizationFilterContext mvcContext) {--#3
            mvcContext.Result = new UnauthorizedResult();
        }*/
        return Task.CompletedTask;
    }
}

另外,我更新了Startup.ConfigureServices(...)

        services.AddAuthorization(o => o.AddPolicy("Test", p => p.Requirements.Add(new TestRequirement())));
        services.AddSingleton<IAuthorizationHandler, TestHandler>();

并且我向控制器添加了适当的属性:[Authorize(Policy = "Test")]

如果我取消注释块 #1 - 它按预期工作(我得到我的数据)。但是当我的代码失败时(我评论#1),我得到 500 Internal Server Error.

然后,我尝试显式使要求失败(取消注释块 #2)- 结果相同。我知道不推荐,但我想试试。

在此之后,我尝试了更丑陋的解决方法,我评论了#2 并取消了#3 块的评论。我得到了相同的 500 状态码。

为了好玩,我实现了具有相同行为的资源过滤器:

public class TestResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context) {
        context.Result = new UnauthorizedResult();
    }

    public void OnResourceExecuted(ResourceExecutedContext context) {
    }
}

然后,我在控制器上将我的授权属性替换为 [TestResourceFilter] 并按预期获得了 401 Unauthorized。但这是使用资源过滤器的糟糕方式。

我的需求实现有什么问题?为什么我得到 500 而不是 401(或 403)?

编辑: 我在日志中找到了 InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.。 我看到了带有 cookies 方案的示例,但它不适合我。因为我要实现无状态调用。

poke 的通讯指出我以错误的方式实现了我的功能。我试图处理授权级别的安全检查,但我必须在身份验证级别上进行。所以我的最终代码看起来像:

public class TestHandlerOptions : AuthenticationSchemeOptions { }

internal class TestHandler : AuthenticationHandler<TestHandlerOptions> {
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
        if (await SomeCheckAsync()) {
            var identity = new ClaimsIdentity(ClaimsName);
            var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), null, ClaimsName);
            return AuthenticateResult.Success(ticket);
        }

        return AuthenticateResult.Fail("Missing or malformed 'Authorization' header.");
    }
}

ConfigureServices中添加下一个在Startup中 class:

services.AddAuthentication(options => options.AddScheme(SchemeName, o => o.HandlerType = typeof(TestHandler)));

并且授权属性看起来像 [Authorize(AuthenticationSchemes = SchemeName)]