如何在 AuthorizationOptions 中设置 FallbackPolicy 时 return HttpStatusCode 404 不正确 url?

How to return HttpStatusCode 404 for incorrect url with FallbackPolicy set in AuthorizationOptions?

在我目前正在处理的 Web 应用程序中,要求所有用户都经过身份验证。目前这是通过使用 AuthorizeFilter.

来处理的

我现在需要能够对应用程序的不同部分应用不同的授权策略,因此我想从使用全局授权过滤器切换到设置回退策略(如官方 documentation).

这按预期工作,除了对现在不存在的资源的请求 returns HttpStatusCode 401 如果未通过身份验证,或 403 如果已通过身份验证但有一些其他要求没有实现(我们在 default/fallback 政策中有一些)。以前,使用授权过滤器解决方案,404 将被 returned。我猜原因是回退策略在管道中比授权过滤器更早被评估,但它仍然是我想避免的副作用。

(如何)我可以在使用 FallbackPolicy 时像以前一样将应用程序发送到 return 404 吗?如果应用程序使用 net5.0(或更高版本),我想我可以使用自定义 IAuthorizationMiddlewareResultHandler,但升级不在短期计划中,这意味着解决方案必须适用于 netcoreapp3.1 .

到 returns 404 回退策略:

app.UseEndpoints(endpoints =>
{
   endpoints.MapControllers();
   endpoints.MapRazorPages();
   endpoints.MapFallbackToController("api/{**slug}", nameof(ErrorController.Error404), "Error");
   endpoints.MapFallbackToPage("{**slug}", "/Public/Errors/404");
});

很明显这个动作用 AllowAnonymous 装饰

    [Route("api/[controller]")]
    [ApiController]
    public class ErrorController : ControllerBase
    {
        [HttpGet]
        [AllowAnonymous]
        public IActionResult Error404()
        {
            return NotFound();
        }
    }

对于剃刀页面:

builder.AddRazorPagesOptions(options =>
{
  options.Conventions.AllowAnonymousToFolder("/Public");

政策

options.FallbackPolicy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .RequireRole(LoginEntities.Sede.ToString())
                    .Build();

options.DefaultPolicy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();

我在使用 netcoreapp3.1 时通过添加扩展 DenyAnonymousAuthorizationRequirement:

的自定义授权处理程序设法获得了我想要的结果
public class CustomDenyAnonymousAuthorizationRequirement : DenyAnonymousAuthorizationRequirement
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DenyAnonymousAuthorizationRequirement requirement)
    {
        if (context.Resource != null)
        {
            return base.HandleRequirementAsync(context, requirement);
        }
        
        context.Succeed(requirement);
        return Task.CompletedTask;
    }
}

如果没有资源,此代码只是将要求标记为已满足。

像这样添加服务集合:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSingleton<IAuthorizationHandler, CustomDenyAnonymousAuthorizationRequirement>();
}

如果使用 net5.0+,可以使用自定义 IAuthorizationMiddlewareResultHandler 来完成同样的事情:

public class CustomAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
    private readonly AuthorizationMiddlewareResultHandler _defaultHandler = new();

    public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
    {
        if ((authorizeResult.Challenged || authorizeResult.Forbidden) && context.GetEndpoint() == null)
        {
            context.Response.StatusCode = (int)HttpStatusCode.NotFound;
            return;
        }

        await _defaultHandler.HandleAsync(next, context, policy, authorizeResult);
    }
}

并添加到服务集合中:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSingleton<IAuthorizationMiddlewareResultHandler, CustomAuthorizationMiddlewareResultHandler>();
}